ext_php_rs/zend/
globals.rs1use std::collections::HashMap;
4use std::ffi::CStr;
5use std::ops::{Deref, DerefMut};
6use std::slice;
7use std::str;
8
9use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
10
11use crate::boxed::ZBox;
12use crate::exception::PhpResult;
13#[cfg(php82)]
14use crate::ffi::zend_atomic_bool_store;
15use crate::ffi::{
16 _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals,
17 ext_php_rs_file_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals,
18 ext_php_rs_sapi_module, php_core_globals, php_file_globals, sapi_globals_struct,
19 sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_ini_entry,
20 zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET,
21 TRACK_VARS_POST, TRACK_VARS_SERVER,
22};
23#[cfg(not(php81))]
24use crate::ffi::{_zend_hash_find_known_hash, _zend_string};
25#[cfg(php81)]
26use crate::ffi::{
27 _zend_known_string_id_ZEND_STR_AUTOGLOBAL_REQUEST, zend_hash_find_known_hash,
28 zend_known_strings,
29};
30
31use crate::types::{ZendHashTable, ZendObject, ZendStr};
32
33use super::linked_list::ZendLinkedListIterator;
34
35pub type ExecutorGlobals = _zend_executor_globals;
37
38pub type SapiModule = _sapi_module_struct;
40
41impl ExecutorGlobals {
42 pub fn get() -> GlobalReadGuard<Self> {
50 let globals = unsafe { ext_php_rs_executor_globals().as_ref() }
53 .expect("Static executor globals were invalid");
54 let guard = GLOBALS_LOCK.read();
55 GlobalReadGuard { globals, guard }
56 }
57
58 pub fn get_mut() -> GlobalWriteGuard<Self> {
66 let globals = unsafe { ext_php_rs_executor_globals().as_mut() }
69 .expect("Static executor globals were invalid");
70 let guard = GLOBALS_LOCK.write();
71 GlobalWriteGuard { globals, guard }
72 }
73
74 pub fn class_table(&self) -> Option<&ZendHashTable> {
76 unsafe { self.class_table.as_ref() }
77 }
78
79 pub fn function_table(&self) -> Option<&ZendHashTable> {
81 unsafe { self.function_table.as_ref() }
82 }
83
84 pub fn function_table_mut(&self) -> Option<&mut ZendHashTable> {
86 unsafe { self.function_table.as_mut() }
87 }
88
89 pub fn ini_values(&self) -> HashMap<String, Option<String>> {
92 let hash_table = unsafe { &*self.ini_directives };
93 let mut ini_hash_map: HashMap<String, Option<String>> = HashMap::new();
94 for (key, value) in hash_table.iter() {
95 ini_hash_map.insert(key.to_string(), unsafe {
96 let ini_entry = &*value.ptr::<zend_ini_entry>().expect("Invalid ini entry");
97 if ini_entry.value.is_null() {
98 None
99 } else {
100 Some(
101 (*ini_entry.value)
102 .as_str()
103 .expect("Ini value is not a string")
104 .to_owned(),
105 )
106 }
107 });
108 }
109 ini_hash_map
110 }
111
112 pub fn constants(&self) -> Option<&ZendHashTable> {
114 unsafe { self.zend_constants.as_ref() }
115 }
116
117 pub fn take_exception() -> Option<ZBox<ZendObject>> {
124 {
125 if Self::get().exception.is_null() {
127 return None;
128 }
129 }
130
131 let mut globals = Self::get_mut();
132
133 let mut exception_ptr = std::ptr::null_mut();
134 std::mem::swap(&mut exception_ptr, &mut globals.exception);
135
136 Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) })
138 }
139
140 pub fn has_exception() -> bool {
142 !Self::get().exception.is_null()
143 }
144
145 pub fn throw_if_exception() -> PhpResult<()> {
152 if let Some(e) = Self::take_exception() {
153 Err(crate::error::Error::Exception(e).into())
154 } else {
155 Ok(())
156 }
157 }
158
159 pub fn request_interrupt(&mut self) {
163 cfg_if::cfg_if! {
164 if #[cfg(php82)] {
165 unsafe {
166 zend_atomic_bool_store(&mut self.vm_interrupt, true);
167 }
168 } else {
169 self.vm_interrupt = true;
170 }
171 }
172 }
173
174 pub fn cancel_interrupt(&mut self) {
176 cfg_if::cfg_if! {
177 if #[cfg(php82)] {
178 unsafe {
179 zend_atomic_bool_store(&mut self.vm_interrupt, false);
180 }
181 } else {
182 self.vm_interrupt = true;
183 }
184 }
185 }
186}
187
188impl SapiModule {
189 pub fn get() -> GlobalReadGuard<Self> {
197 let globals = unsafe { ext_php_rs_sapi_module().as_ref() }
200 .expect("Static executor globals were invalid");
201 let guard = SAPI_MODULE_LOCK.read();
202 GlobalReadGuard { globals, guard }
203 }
204
205 pub fn get_mut() -> GlobalWriteGuard<Self> {
213 let globals = unsafe { ext_php_rs_sapi_module().as_mut() }
216 .expect("Static executor globals were invalid");
217 let guard = SAPI_MODULE_LOCK.write();
218 GlobalWriteGuard { globals, guard }
219 }
220}
221
222pub type ProcessGlobals = php_core_globals;
224
225impl ProcessGlobals {
226 pub fn get() -> GlobalReadGuard<Self> {
234 let globals = unsafe { &*ext_php_rs_process_globals() };
237 let guard = PROCESS_GLOBALS_LOCK.read();
238 GlobalReadGuard { globals, guard }
239 }
240
241 pub fn get_mut() -> GlobalWriteGuard<Self> {
249 let globals = unsafe { &mut *ext_php_rs_process_globals() };
252 let guard = PROCESS_GLOBALS_LOCK.write();
253 GlobalWriteGuard { globals, guard }
254 }
255
256 pub fn http_server_vars(&self) -> Option<&ZendHashTable> {
258 if !self.http_globals[TRACK_VARS_SERVER as usize].is_array() {
261 let name = ZendStr::new("_SERVER", false).as_mut_ptr();
262 unsafe { zend_is_auto_global(name) };
263 }
264 if self.http_globals[TRACK_VARS_SERVER as usize].is_array() {
265 self.http_globals[TRACK_VARS_SERVER as usize].array()
266 } else {
267 None
268 }
269 }
270
271 pub fn http_post_vars(&self) -> &ZendHashTable {
273 self.http_globals[TRACK_VARS_POST as usize]
274 .array()
275 .expect("Type is not a ZendArray")
276 }
277
278 pub fn http_get_vars(&self) -> &ZendHashTable {
280 self.http_globals[TRACK_VARS_GET as usize]
281 .array()
282 .expect("Type is not a ZendArray")
283 }
284
285 pub fn http_cookie_vars(&self) -> &ZendHashTable {
287 self.http_globals[TRACK_VARS_COOKIE as usize]
288 .array()
289 .expect("Type is not a ZendArray")
290 }
291
292 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 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"))
328 }
329
330 pub fn http_env_vars(&self) -> &ZendHashTable {
332 self.http_globals[TRACK_VARS_ENV as usize]
333 .array()
334 .expect("Type is not a ZendArray")
335 }
336
337 pub fn http_files_vars(&self) -> &ZendHashTable {
339 self.http_globals[TRACK_VARS_FILES as usize]
340 .array()
341 .expect("Type is not a ZendArray")
342 }
343}
344
345pub type SapiGlobals = sapi_globals_struct;
347
348impl SapiGlobals {
349 pub fn get() -> GlobalReadGuard<Self> {
357 let globals = unsafe { &*ext_php_rs_sapi_globals() };
360 let guard = SAPI_GLOBALS_LOCK.read();
361 GlobalReadGuard { globals, guard }
362 }
363
364 pub fn get_mut() -> GlobalWriteGuard<Self> {
372 let globals = unsafe { &mut *ext_php_rs_sapi_globals() };
375 let guard = SAPI_GLOBALS_LOCK.write();
376 GlobalWriteGuard { globals, guard }
377 }
378 pub fn request_info(&self) -> &SapiRequestInfo {
380 &self.request_info
381 }
382
383 pub fn sapi_headers(&self) -> &SapiHeaders {
384 &self.sapi_headers
385 }
386}
387
388pub type SapiHeaders = sapi_headers_struct;
389
390impl<'a> SapiHeaders {
391 pub fn headers(&'a mut self) -> ZendLinkedListIterator<'a, SapiHeader> {
392 self.headers.iter()
393 }
394}
395
396pub type SapiHeader = sapi_header_struct;
397
398impl<'a> SapiHeader {
399 pub fn as_str(&'a self) -> &'a str {
400 unsafe {
401 let slice = slice::from_raw_parts(self.header as *const u8, self.header_len);
402 str::from_utf8(slice).expect("Invalid header string")
403 }
404 }
405
406 pub fn name(&'a self) -> &'a str {
407 self.as_str().split(':').next().unwrap_or("").trim()
408 }
409
410 pub fn value(&'a self) -> Option<&'a str> {
411 self.as_str().split(':').nth(1).map(|s| s.trim())
412 }
413}
414
415pub type SapiRequestInfo = sapi_request_info;
416
417impl SapiRequestInfo {
418 pub fn request_method(&self) -> Option<&str> {
419 if self.request_method.is_null() {
420 return None;
421 }
422 unsafe { CStr::from_ptr(self.request_method).to_str().ok() }
423 }
424
425 pub fn query_string(&self) -> Option<&str> {
426 if self.query_string.is_null() {
427 return None;
428 }
429 unsafe { CStr::from_ptr(self.query_string).to_str().ok() }
430 }
431
432 pub fn cookie_data(&self) -> Option<&str> {
433 if self.cookie_data.is_null() {
434 return None;
435 }
436 unsafe { CStr::from_ptr(self.cookie_data).to_str().ok() }
437 }
438
439 pub fn content_length(&self) -> i64 {
440 self.content_length
441 }
442
443 pub fn path_translated(&self) -> Option<&str> {
444 if self.path_translated.is_null() {
445 return None;
446 }
447 unsafe { CStr::from_ptr(self.path_translated).to_str().ok() }
448 }
449
450 pub fn request_uri(&self) -> Option<&str> {
451 if self.request_uri.is_null() {
452 return None;
453 }
454 unsafe { CStr::from_ptr(self.request_uri).to_str().ok() }
455 }
456
457 pub fn content_type(&self) -> Option<&str> {
460 if self.content_type.is_null() {
461 return None;
462 }
463 unsafe { CStr::from_ptr(self.content_type).to_str().ok() }
464 }
465
466 pub fn headers_only(&self) -> bool {
467 self.headers_only
468 }
469
470 pub fn no_headers(&self) -> bool {
471 self.no_headers
472 }
473
474 pub fn headers_read(&self) -> bool {
475 self.headers_read
476 }
477
478 pub fn auth_user(&self) -> Option<&str> {
481 if self.auth_user.is_null() {
482 return None;
483 }
484 unsafe { CStr::from_ptr(self.auth_user).to_str().ok() }
485 }
486
487 pub fn auth_password(&self) -> Option<&str> {
488 if self.auth_password.is_null() {
489 return None;
490 }
491 unsafe { CStr::from_ptr(self.auth_password).to_str().ok() }
492 }
493
494 pub fn auth_digest(&self) -> Option<&str> {
495 if self.auth_digest.is_null() {
496 return None;
497 }
498 unsafe { CStr::from_ptr(self.auth_digest).to_str().ok() }
499 }
500
501 pub fn argv0(&self) -> Option<&str> {
502 if self.argv0.is_null() {
503 return None;
504 }
505 unsafe { CStr::from_ptr(self.argv0).to_str().ok() }
506 }
507
508 pub fn current_user(&self) -> Option<&str> {
509 if self.current_user.is_null() {
510 return None;
511 }
512 unsafe { CStr::from_ptr(self.current_user).to_str().ok() }
513 }
514
515 pub fn current_user_length(&self) -> i32 {
516 self.current_user_length
517 }
518
519 pub fn argvc(&self) -> i32 {
520 self.argc
521 }
522
523 pub fn argv(&self) -> Option<&str> {
524 if self.argv.is_null() {
525 return None;
526 }
527 unsafe { CStr::from_ptr(*self.argv).to_str().ok() }
528 }
529
530 pub fn proto_num(&self) -> i32 {
531 self.proto_num
532 }
533}
534
535pub type FileGlobals = php_file_globals;
537
538impl FileGlobals {
539 pub fn get() -> GlobalReadGuard<Self> {
547 let globals = unsafe { ext_php_rs_file_globals().as_ref() }
550 .expect("Static file globals were invalid");
551 let guard = FILE_GLOBALS_LOCK.read();
552 GlobalReadGuard { globals, guard }
553 }
554
555 pub fn get_mut() -> GlobalWriteGuard<Self> {
563 let globals = unsafe { &mut *ext_php_rs_file_globals() };
566 let guard = SAPI_GLOBALS_LOCK.write();
567 GlobalWriteGuard { globals, guard }
568 }
569
570 pub fn stream_wrappers(&self) -> Option<&'static ZendHashTable> {
571 unsafe { self.stream_wrappers.as_ref() }
572 }
573}
574
575static GLOBALS_LOCK: RwLock<()> = const_rwlock(());
580static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
581static SAPI_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
582static FILE_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
583
584static SAPI_MODULE_LOCK: RwLock<()> = const_rwlock(());
589
590pub struct GlobalReadGuard<T: 'static> {
593 globals: &'static T,
594 #[allow(dead_code)]
595 guard: RwLockReadGuard<'static, ()>,
596}
597
598impl<T> Deref for GlobalReadGuard<T> {
599 type Target = T;
600
601 fn deref(&self) -> &Self::Target {
602 self.globals
603 }
604}
605
606pub struct GlobalWriteGuard<T: 'static> {
609 globals: &'static mut T,
610 #[allow(dead_code)]
611 guard: RwLockWriteGuard<'static, ()>,
612}
613
614impl<T> Deref for GlobalWriteGuard<T> {
615 type Target = T;
616
617 fn deref(&self) -> &Self::Target {
618 self.globals
619 }
620}
621
622impl<T> DerefMut for GlobalWriteGuard<T> {
623 fn deref_mut(&mut self) -> &mut Self::Target {
624 self.globals
625 }
626}