Merge pull request #249 from davidcole1340/process-globals

Add streams API, ProcessGlobals, FileGlobals and SapiGlobals
This commit is contained in:
Daniil Gentili 2023-11-24 14:33:37 +01:00 committed by GitHub
commit 5de7c7f167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 738 additions and 52 deletions

View File

@ -245,7 +245,6 @@ bind! {
zend_class_serialize_deny,
zend_class_unserialize_deny,
zend_executor_globals,
sapi_globals_struct,
sapi_module_struct,
zend_objects_store_del,
zend_hash_move_forward_ex,
@ -257,13 +256,39 @@ bind! {
gc_possible_root,
ZEND_ACC_NOT_SERIALIZABLE,
executor_globals,
php_core_globals,
core_globals,
sapi_globals_struct,
sapi_globals,
sapi_module,
php_printf,
__zend_malloc,
tsrm_get_ls_cache,
executor_globals_offset,
core_globals_offset,
sapi_globals_offset,
php_file_globals,
file_globals,
file_globals_id,
TRACK_VARS_POST,
TRACK_VARS_GET,
TRACK_VARS_COOKIE,
TRACK_VARS_SERVER,
TRACK_VARS_ENV,
TRACK_VARS_FILES,
TRACK_VARS_REQUEST,
sapi_request_info,
sapi_header_struct,
zend_is_auto_global,
zend_llist_get_next_ex,
zend_llist_get_prev_ex,
php_register_url_stream_wrapper,
php_stream_locate_url_wrapper,
php_unregister_url_stream_wrapper,
php_unregister_url_stream_wrapper_volatile,
php_register_url_stream_wrapper_volatile,
php_stream_wrapper,
php_stream_stdio_ops,
zend_atomic_bool_store,
zend_interrupt_function,
zend_eval_string,

View File

@ -182,6 +182,13 @@ pub const ZEND_MODULE_API_NO: u32 = 20230831;
pub const USING_ZTS: u32 = 0;
pub const MAY_BE_BOOL: u32 = 12;
pub const MAY_BE_ANY: u32 = 1022;
pub const TRACK_VARS_POST: u32 = 0;
pub const TRACK_VARS_GET: u32 = 1;
pub const TRACK_VARS_COOKIE: u32 = 2;
pub const TRACK_VARS_SERVER: u32 = 3;
pub const TRACK_VARS_ENV: u32 = 4;
pub const TRACK_VARS_FILES: u32 = 5;
pub const TRACK_VARS_REQUEST: u32 = 6;
pub const PHP_INI_USER: u32 = 1;
pub const PHP_INI_PERDIR: u32 = 2;
pub const PHP_INI_SYSTEM: u32 = 4;
@ -506,6 +513,19 @@ pub struct _zend_llist {
pub traverse_ptr: *mut zend_llist_element,
}
pub type zend_llist = _zend_llist;
pub type zend_llist_position = *mut zend_llist_element;
extern "C" {
pub fn zend_llist_get_next_ex(
l: *mut zend_llist,
pos: *mut zend_llist_position,
) -> *mut ::std::os::raw::c_void;
}
extern "C" {
pub fn zend_llist_get_prev_ex(
l: *mut zend_llist,
pos: *mut zend_llist_position,
) -> *mut ::std::os::raw::c_void;
}
pub type zend_string_init_interned_func_t = ::std::option::Option<
unsafe extern "C" fn(
str_: *const ::std::os::raw::c_char,
@ -1469,6 +1489,9 @@ pub struct _zend_executor_globals {
pub reserved_stack_size: zend_ulong,
pub reserved: [*mut ::std::os::raw::c_void; 6usize],
}
extern "C" {
pub fn zend_is_auto_global(name: *mut zend_string) -> bool;
}
pub type zend_module_entry = _zend_module_entry;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
@ -2094,6 +2117,127 @@ impl _php_stream {
__bindgen_bitfield_unit
}
}
extern "C" {
pub static mut php_stream_stdio_ops: php_stream_ops;
}
extern "C" {
pub fn php_register_url_stream_wrapper(
protocol: *const ::std::os::raw::c_char,
wrapper: *const php_stream_wrapper,
) -> zend_result;
}
extern "C" {
pub fn php_unregister_url_stream_wrapper(
protocol: *const ::std::os::raw::c_char,
) -> zend_result;
}
extern "C" {
pub fn php_register_url_stream_wrapper_volatile(
protocol: *mut zend_string,
wrapper: *mut php_stream_wrapper,
) -> zend_result;
}
extern "C" {
pub fn php_unregister_url_stream_wrapper_volatile(protocol: *mut zend_string) -> zend_result;
}
extern "C" {
pub fn php_stream_locate_url_wrapper(
path: *const ::std::os::raw::c_char,
path_for_open: *mut *const ::std::os::raw::c_char,
options: ::std::os::raw::c_int,
) -> *mut php_stream_wrapper;
}
pub type php_core_globals = _php_core_globals;
#[repr(C)]
pub struct _php_core_globals {
pub output_buffering: zend_long,
pub implicit_flush: bool,
pub enable_dl: bool,
pub display_errors: u8,
pub display_startup_errors: bool,
pub log_errors: bool,
pub ignore_repeated_errors: bool,
pub ignore_repeated_source: bool,
pub report_memleaks: bool,
pub output_handler: *mut ::std::os::raw::c_char,
pub unserialize_callback_func: *mut ::std::os::raw::c_char,
pub serialize_precision: zend_long,
pub memory_limit: zend_long,
pub max_input_time: zend_long,
pub error_log: *mut ::std::os::raw::c_char,
pub doc_root: *mut ::std::os::raw::c_char,
pub user_dir: *mut ::std::os::raw::c_char,
pub include_path: *mut ::std::os::raw::c_char,
pub open_basedir: *mut ::std::os::raw::c_char,
pub open_basedir_modified: bool,
pub extension_dir: *mut ::std::os::raw::c_char,
pub php_binary: *mut ::std::os::raw::c_char,
pub sys_temp_dir: *mut ::std::os::raw::c_char,
pub upload_tmp_dir: *mut ::std::os::raw::c_char,
pub upload_max_filesize: zend_long,
pub error_append_string: *mut ::std::os::raw::c_char,
pub error_prepend_string: *mut ::std::os::raw::c_char,
pub auto_prepend_file: *mut ::std::os::raw::c_char,
pub auto_append_file: *mut ::std::os::raw::c_char,
pub input_encoding: *mut ::std::os::raw::c_char,
pub internal_encoding: *mut ::std::os::raw::c_char,
pub output_encoding: *mut ::std::os::raw::c_char,
pub arg_separator: arg_separators,
pub variables_order: *mut ::std::os::raw::c_char,
pub rfc1867_protected_variables: HashTable,
pub connection_status: ::std::os::raw::c_short,
pub ignore_user_abort: bool,
pub header_is_being_sent: ::std::os::raw::c_uchar,
pub tick_functions: zend_llist,
pub http_globals: [zval; 6usize],
pub expose_php: bool,
pub register_argc_argv: bool,
pub auto_globals_jit: bool,
pub html_errors: bool,
pub xmlrpc_errors: bool,
pub docref_root: *mut ::std::os::raw::c_char,
pub docref_ext: *mut ::std::os::raw::c_char,
pub xmlrpc_error_number: zend_long,
pub activated_auto_globals: [bool; 8usize],
pub modules_activated: bool,
pub file_uploads: bool,
pub during_request_startup: bool,
pub allow_url_fopen: bool,
pub enable_post_data_reading: bool,
pub report_zend_debug: bool,
pub last_error_type: ::std::os::raw::c_int,
pub last_error_lineno: ::std::os::raw::c_int,
pub last_error_message: *mut zend_string,
pub last_error_file: *mut zend_string,
pub php_sys_temp_dir: *mut ::std::os::raw::c_char,
pub disable_classes: *mut ::std::os::raw::c_char,
pub max_input_nesting_level: zend_long,
pub max_input_vars: zend_long,
pub user_ini_filename: *mut ::std::os::raw::c_char,
pub user_ini_cache_ttl: zend_long,
pub request_order: *mut ::std::os::raw::c_char,
pub mail_log: *mut ::std::os::raw::c_char,
pub mail_x_header: bool,
pub mail_mixed_lf_and_crlf: bool,
pub in_error_log: bool,
pub allow_url_include: bool,
pub in_user_include: bool,
pub have_called_openlog: bool,
pub syslog_facility: zend_long,
pub syslog_ident: *mut ::std::os::raw::c_char,
pub syslog_filter: zend_long,
pub error_log_mode: zend_long,
}
extern "C" {
pub static mut core_globals: _php_core_globals;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _arg_separators {
pub output: *mut ::std::os::raw::c_char,
pub input: *mut ::std::os::raw::c_char,
}
pub type arg_separators = _arg_separators;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _zend_ini_entry_def {
@ -2201,6 +2345,37 @@ extern "C" {
extern "C" {
pub fn php_info_print_table_end();
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct hostent {
pub h_name: *mut ::std::os::raw::c_char,
pub h_aliases: *mut *mut ::std::os::raw::c_char,
pub h_addrtype: ::std::os::raw::c_int,
pub h_length: ::std::os::raw::c_int,
pub h_addr_list: *mut *mut ::std::os::raw::c_char,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct php_file_globals {
pub pclose_ret: ::std::os::raw::c_int,
pub def_chunk_size: usize,
pub auto_detect_line_endings: bool,
pub default_socket_timeout: zend_long,
pub user_agent: *mut ::std::os::raw::c_char,
pub from_address: *mut ::std::os::raw::c_char,
pub user_stream_current_filename: *const ::std::os::raw::c_char,
pub default_context: *mut php_stream_context,
pub stream_wrappers: *mut HashTable,
pub stream_filters: *mut HashTable,
pub wrapper_errors: *mut HashTable,
pub pclose_wait: ::std::os::raw::c_int,
pub tmp_host_info: hostent,
pub tmp_host_buf: *mut ::std::os::raw::c_char,
pub tmp_host_buf_len: usize,
}
extern "C" {
pub static mut file_globals: php_file_globals;
}
extern "C" {
pub static mut zend_ce_throwable: *mut zend_class_entry;
}

View File

@ -65,6 +65,10 @@ pub enum Error {
IntegerOverflow,
/// An exception was thrown in a function.
Exception(ZBox<ZendObject>),
/// A failure occurred while registering the stream wrapper
StreamWrapperRegistrationFailure,
/// A failure occurred while unregistering the stream wrapper
StreamWrapperUnregistrationFailure,
}
impl Display for Error {
@ -99,6 +103,15 @@ impl Display for Error {
write!(f, "Converting integer arguments resulted in an overflow.")
}
Error::Exception(e) => write!(f, "Exception was thrown: {e:?}"),
Error::StreamWrapperRegistrationFailure => {
write!(f, "A failure occurred while registering the stream wrapper")
}
Error::StreamWrapperUnregistrationFailure => {
write!(
f,
"A failure occurred while unregistering the stream wrapper"
)
}
}
}
}

View File

@ -69,8 +69,8 @@ impl PhpException {
/// Set the Zval object for the exception.
///
/// Exceptions can be based of instantiated Zval objects when you are throwing a custom exception with
/// stateful properties.
/// Exceptions can be based of instantiated Zval objects when you are
/// throwing a custom exception with stateful properties.
///
/// # Parameters
///

View File

@ -26,7 +26,9 @@ extern "C" {
pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void;
pub fn ext_php_rs_zend_object_release(obj: *mut zend_object);
pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals;
pub fn ext_php_rs_process_globals() -> *mut php_core_globals;
pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct;
pub fn ext_php_rs_file_globals() -> *mut php_file_globals;
pub fn ext_php_rs_sapi_module() -> *mut sapi_module_struct;
pub fn ext_php_rs_zend_try_catch(
func: unsafe extern "C" fn(*const c_void) -> *const c_void,

View File

@ -222,7 +222,8 @@ impl Zval {
}
}
/// Returns a mutable reference to the zval if it is an internal indirect reference.
/// Returns a mutable reference to the zval if it is an internal indirect
/// reference.
pub fn indirect_mut(&self) -> Option<&mut Zval> {
if self.is_indirect() {
Some(unsafe { &mut *(self.value.zv as *mut Zval) })

View File

@ -40,6 +40,18 @@ zend_executor_globals *ext_php_rs_executor_globals() {
#endif
}
php_core_globals *ext_php_rs_process_globals() {
#ifdef ZTS
#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE
return TSRMG_FAST_BULK_STATIC(core_globals_offset, php_core_globals);
#else
return TSRMG_FAST_BULK(core_globals_offset, php_core_globals *);
#endif
#else
return &core_globals;
#endif
}
sapi_globals_struct *ext_php_rs_sapi_globals() {
#ifdef ZTS
#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE
@ -52,6 +64,14 @@ sapi_globals_struct *ext_php_rs_sapi_globals() {
#endif
}
php_file_globals *ext_php_rs_file_globals() {
#ifdef ZTS
return TSRMG_FAST_BULK(file_globals_id, php_file_globals *);
#else
return &file_globals;
#endif
}
sapi_module_struct *ext_php_rs_sapi_module() {
return &sapi_module;
}

View File

@ -17,9 +17,12 @@
#include "php.h"
#include "ext/standard/info.h"
#include "ext/standard/php_var.h"
#include "ext/standard/file.h"
#include "zend_exceptions.h"
#include "zend_inheritance.h"
#include "zend_interfaces.h"
#include "php_variables.h"
#include "zend_ini.h"
#include "main/SAPI.h"
@ -31,8 +34,10 @@ void ext_php_rs_set_known_valid_utf8(zend_string *zs);
const char *ext_php_rs_php_build_id();
void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce);
void ext_php_rs_zend_object_release(zend_object *obj);
zend_executor_globals *ext_php_rs_executor_globals();;
zend_executor_globals *ext_php_rs_executor_globals();
php_core_globals *ext_php_rs_process_globals();
sapi_globals_struct *ext_php_rs_sapi_globals();
php_file_globals *ext_php_rs_file_globals();
sapi_module_struct *ext_php_rs_sapi_module();
bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result);
void ext_php_rs_zend_bailout();

View File

@ -1,7 +1,10 @@
//! Types related to the PHP executor globals.
//! Types related to the PHP executor, sapi and process globals.
use std::collections::HashMap;
use std::ffi::CStr;
use std::ops::{Deref, DerefMut};
use std::slice;
use std::str;
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
@ -9,17 +12,21 @@ use crate::boxed::ZBox;
#[cfg(php82)]
use crate::ffi::zend_atomic_bool_store;
use crate::ffi::{
_sapi_globals_struct, _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals,
ext_php_rs_sapi_globals, ext_php_rs_sapi_module, zend_ini_entry,
_sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals,
ext_php_rs_file_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals,
ext_php_rs_sapi_module, php_core_globals, php_file_globals, sapi_globals_struct,
sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_ini_entry,
zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET,
TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER,
};
use crate::types::{ZendHashTable, ZendObject};
use crate::types::{ZendHashTable, ZendObject, ZendStr};
use super::linked_list::ZendLinkedListIterator;
/// Stores global variables used in the PHP executor.
pub type ExecutorGlobals = _zend_executor_globals;
/// Stores global SAPI variables used in the PHP executor.
pub type SapiGlobals = _sapi_globals_struct;
/// Stores the SAPI module used in the PHP executor.
pub type SapiModule = _sapi_module_struct;
@ -146,40 +153,6 @@ impl ExecutorGlobals {
}
}
impl SapiGlobals {
/// Returns a reference to the PHP SAPI globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get() -> GlobalReadGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { ext_php_rs_sapi_globals().as_ref() }
.expect("Static executor globals were invalid");
let guard = SAPI_LOCK.read();
GlobalReadGuard { globals, guard }
}
/// Returns a mutable reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get_mut() -> GlobalWriteGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { ext_php_rs_sapi_globals().as_mut() }
.expect("Static executor globals were invalid");
let guard = SAPI_LOCK.write();
GlobalWriteGuard { globals, guard }
}
}
impl SapiModule {
/// Returns a reference to the PHP SAPI module.
///
@ -214,17 +187,336 @@ impl SapiModule {
}
}
/// Stores global variables used in the PHP executor.
pub type ProcessGlobals = php_core_globals;
impl ProcessGlobals {
/// Returns a reference to the PHP process globals.
///
/// The process globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get() -> GlobalReadGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { &*ext_php_rs_process_globals() };
let guard = PROCESS_GLOBALS_LOCK.read();
GlobalReadGuard { globals, guard }
}
/// Returns a mutable reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get_mut() -> GlobalWriteGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { &mut *ext_php_rs_process_globals() };
let guard = PROCESS_GLOBALS_LOCK.write();
GlobalWriteGuard { globals, guard }
}
/// Get the HTTP Server variables. Equivalent of $_SERVER.
pub fn http_server_vars(&self) -> Option<&ZendHashTable> {
// $_SERVER is lazy-initted, we need to call zend_is_auto_global
// if it's not already populated.
if !self.http_globals[TRACK_VARS_SERVER as usize].is_array() {
let name = ZendStr::new("_SERVER", false).as_mut_ptr();
unsafe { zend_is_auto_global(name) };
}
if self.http_globals[TRACK_VARS_SERVER as usize].is_array() {
self.http_globals[TRACK_VARS_SERVER as usize].array()
} else {
None
}
}
/// Get the HTTP POST variables. Equivalent of $_POST.
pub fn http_post_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_POST as usize]
.array()
.expect("Type is not a ZendArray")
}
/// Get the HTTP GET variables. Equivalent of $_GET.
pub fn http_get_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_GET as usize]
.array()
.expect("Type is not a ZendArray")
}
/// Get the HTTP Cookie variables. Equivalent of $_COOKIE.
pub fn http_cookie_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_COOKIE as usize]
.array()
.expect("Type is not a ZendArray")
}
/// Get the HTTP Request variables. Equivalent of $_REQUEST.
pub fn http_request_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_REQUEST as usize]
.array()
.expect("Type is not a ZendArray")
}
/// Get the HTTP Environment variables. Equivalent of $_ENV.
pub fn http_env_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_ENV as usize]
.array()
.expect("Type is not a ZendArray")
}
/// Get the HTTP Files variables. Equivalent of $_FILES.
pub fn http_files_vars(&self) -> &ZendHashTable {
self.http_globals[TRACK_VARS_FILES as usize]
.array()
.expect("Type is not a ZendArray")
}
}
/// Stores global variables used in the SAPI.
pub type SapiGlobals = sapi_globals_struct;
impl SapiGlobals {
/// Returns a reference to the PHP process globals.
///
/// The process globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get() -> GlobalReadGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { &*ext_php_rs_sapi_globals() };
let guard = SAPI_GLOBALS_LOCK.read();
GlobalReadGuard { globals, guard }
}
/// Returns a mutable reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get_mut() -> GlobalWriteGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { &mut *ext_php_rs_sapi_globals() };
let guard = SAPI_GLOBALS_LOCK.write();
GlobalWriteGuard { globals, guard }
}
// Get the request info for the Sapi.
pub fn request_info(&self) -> &SapiRequestInfo {
&self.request_info
}
pub fn sapi_headers(&self) -> &SapiHeaders {
&self.sapi_headers
}
}
pub type SapiHeaders = sapi_headers_struct;
impl<'a> SapiHeaders {
pub fn headers(&'a mut self) -> ZendLinkedListIterator<'a, SapiHeader> {
self.headers.iter()
}
}
pub type SapiHeader = sapi_header_struct;
impl<'a> SapiHeader {
pub fn as_str(&'a self) -> &'a str {
unsafe {
let slice = slice::from_raw_parts(self.header as *const u8, self.header_len);
str::from_utf8(slice).expect("Invalid header string")
}
}
pub fn name(&'a self) -> &'a str {
self.as_str().split(':').next().unwrap_or("").trim()
}
pub fn value(&'a self) -> Option<&'a str> {
self.as_str().split(':').nth(1).map(|s| s.trim())
}
}
pub type SapiRequestInfo = sapi_request_info;
impl SapiRequestInfo {
pub fn request_method(&self) -> Option<&str> {
if self.request_method.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.request_method).to_str().ok() }
}
pub fn query_string(&self) -> Option<&str> {
if self.query_string.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.query_string).to_str().ok() }
}
pub fn cookie_data(&self) -> Option<&str> {
if self.cookie_data.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.cookie_data).to_str().ok() }
}
pub fn content_length(&self) -> i64 {
self.content_length
}
pub fn path_translated(&self) -> Option<&str> {
if self.path_translated.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.path_translated).to_str().ok() }
}
pub fn request_uri(&self) -> Option<&str> {
if self.request_uri.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.request_uri).to_str().ok() }
}
// Todo: request_body _php_stream
pub fn content_type(&self) -> Option<&str> {
if self.content_type.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.content_type).to_str().ok() }
}
pub fn headers_only(&self) -> bool {
self.headers_only
}
pub fn no_headers(&self) -> bool {
self.no_headers
}
pub fn headers_read(&self) -> bool {
self.headers_read
}
// Todo: post_entry sapi_post_entry
pub fn auth_user(&self) -> Option<&str> {
if self.auth_user.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.auth_user).to_str().ok() }
}
pub fn auth_password(&self) -> Option<&str> {
if self.auth_password.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.auth_password).to_str().ok() }
}
pub fn auth_digest(&self) -> Option<&str> {
if self.auth_digest.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.auth_digest).to_str().ok() }
}
pub fn argv0(&self) -> Option<&str> {
if self.argv0.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.argv0).to_str().ok() }
}
pub fn current_user(&self) -> Option<&str> {
if self.current_user.is_null() {
return None;
}
unsafe { CStr::from_ptr(self.current_user).to_str().ok() }
}
pub fn current_user_length(&self) -> i32 {
self.current_user_length
}
pub fn argvc(&self) -> i32 {
self.argc
}
pub fn argv(&self) -> Option<&str> {
if self.argv.is_null() {
return None;
}
unsafe { CStr::from_ptr(*self.argv).to_str().ok() }
}
pub fn proto_num(&self) -> i32 {
self.proto_num
}
}
/// Stores global variables used in the SAPI.
pub type FileGlobals = php_file_globals;
impl FileGlobals {
/// Returns a reference to the PHP process globals.
///
/// The process globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get() -> GlobalReadGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { ext_php_rs_file_globals().as_ref() }
.expect("Static file globals were invalid");
let guard = FILE_GLOBALS_LOCK.read();
GlobalReadGuard { globals, guard }
}
/// Returns a mutable reference to the PHP executor globals.
///
/// The executor globals are guarded by a RwLock. There can be multiple
/// immutable references at one time but only ever one mutable reference.
/// Attempting to retrieve the globals while already holding the global
/// guard will lead to a deadlock. Dropping the globals guard will release
/// the lock.
pub fn get_mut() -> GlobalWriteGuard<Self> {
// SAFETY: PHP executor globals are statically declared therefore should never
// return an invalid pointer.
let globals = unsafe { &mut *ext_php_rs_file_globals() };
let guard = SAPI_GLOBALS_LOCK.write();
GlobalWriteGuard { globals, guard }
}
pub fn stream_wrappers(&self) -> Option<&'static ZendHashTable> {
unsafe { self.stream_wrappers.as_ref() }
}
}
/// Executor globals rwlock.
///
/// PHP provides no indication if the executor globals are being accessed so
/// this is only effective on the Rust side.
static GLOBALS_LOCK: RwLock<()> = const_rwlock(());
/// SAPI globals rwlock.
///
/// PHP provides no indication if the executor globals are being accessed so
/// this is only effective on the Rust side.
static SAPI_LOCK: RwLock<()> = const_rwlock(());
static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
static SAPI_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
static FILE_GLOBALS_LOCK: RwLock<()> = const_rwlock(());
/// SAPI globals rwlock.
///

46
src/zend/linked_list.rs Normal file
View File

@ -0,0 +1,46 @@
use std::marker::PhantomData;
use crate::ffi::{zend_llist, zend_llist_element, zend_llist_get_next_ex};
pub type ZendLinkedList = zend_llist;
impl ZendLinkedList {
pub fn iter<T>(&self) -> ZendLinkedListIterator<T> {
ZendLinkedListIterator::new(self)
}
}
pub struct ZendLinkedListIterator<'a, T> {
list: &'a zend_llist,
position: *mut zend_llist_element,
_marker: PhantomData<T>,
}
impl<'a, T> ZendLinkedListIterator<'a, T> {
fn new(list: &'a ZendLinkedList) -> Self {
ZendLinkedListIterator {
list,
position: list.head,
_marker: PhantomData,
}
}
}
impl<'a, T: 'a> Iterator for ZendLinkedListIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.position.is_null() {
return None;
}
let ptr = unsafe { (*self.position).data.as_mut_ptr() };
let value = unsafe { &*(ptr as *const T as *mut T) };
unsafe {
zend_llist_get_next_ex(
self.list as *const ZendLinkedList as *mut ZendLinkedList,
&mut self.position,
)
};
Some(value)
}
}

View File

@ -8,7 +8,9 @@ mod function;
mod globals;
mod handlers;
mod ini_entry_def;
mod linked_list;
mod module;
mod streams;
mod try_catch;
use crate::{
@ -23,11 +25,15 @@ pub use ex::ExecuteData;
pub use function::Function;
pub use function::FunctionEntry;
pub use globals::ExecutorGlobals;
pub use globals::FileGlobals;
pub use globals::ProcessGlobals;
pub use globals::SapiGlobals;
pub use globals::SapiModule;
pub use handlers::ZendObjectHandlers;
pub use ini_entry_def::IniEntryDef;
pub use linked_list::ZendLinkedList;
pub use module::ModuleEntry;
pub use streams::*;
#[cfg(feature = "embed")]
pub(crate) use try_catch::panic_wrapper;
pub use try_catch::{bailout, try_catch};

101
src/zend/streams.rs Normal file
View File

@ -0,0 +1,101 @@
use std::ptr::{self, NonNull};
use crate::{
error::Error,
ffi::{
php_register_url_stream_wrapper, php_register_url_stream_wrapper_volatile, php_stream,
php_stream_context, php_stream_locate_url_wrapper, php_stream_wrapper,
php_stream_wrapper_ops, php_unregister_url_stream_wrapper,
php_unregister_url_stream_wrapper_volatile, zend_string,
},
types::ZendStr,
};
pub type StreamWrapper = php_stream_wrapper;
pub type StreamOpener = unsafe extern "C" fn(
*mut StreamWrapper,
*const std::ffi::c_char,
*const std::ffi::c_char,
i32,
*mut *mut zend_string,
*mut php_stream_context,
i32,
*const std::ffi::c_char,
u32,
*const std::ffi::c_char,
u32,
) -> *mut Stream;
impl StreamWrapper {
pub fn get(name: &str) -> Option<&Self> {
unsafe {
let result = php_stream_locate_url_wrapper(name.as_ptr().cast(), ptr::null_mut(), 0);
Some(NonNull::new(result)?.as_ref())
}
}
pub fn get_mut(name: &str) -> Option<&mut Self> {
unsafe {
let result = php_stream_locate_url_wrapper(name.as_ptr().cast(), ptr::null_mut(), 0);
Some(NonNull::new(result)?.as_mut())
}
}
pub fn register(self, name: &str) -> Result<Self, Error> {
// We have to convert it to a static so owned streamwrapper doesn't get dropped.
let copy = Box::new(self);
let copy = Box::leak(copy);
let name = std::ffi::CString::new(name).expect("Could not create C string for name!");
let result = unsafe { php_register_url_stream_wrapper(name.as_ptr(), copy) };
if result == 0 {
Ok(*copy)
} else {
Err(Error::StreamWrapperRegistrationFailure)
}
}
pub fn register_volatile(self, name: &str) -> Result<Self, Error> {
// We have to convert it to a static so owned streamwrapper doesn't get dropped.
let copy = Box::new(self);
let copy = Box::leak(copy);
let name = ZendStr::new(name, false);
let result =
unsafe { php_register_url_stream_wrapper_volatile((*name).as_ptr() as _, copy) };
if result == 0 {
Ok(*copy)
} else {
Err(Error::StreamWrapperRegistrationFailure)
}
}
pub fn unregister(name: &str) -> Result<(), Error> {
let name = std::ffi::CString::new(name).expect("Could not create C string for name!");
match unsafe { php_unregister_url_stream_wrapper(name.as_ptr()) } {
0 => Ok(()),
_ => Err(Error::StreamWrapperUnregistrationFailure),
}
}
pub fn unregister_volatile(name: &str) -> Result<(), Error> {
let name = ZendStr::new(name, false);
match unsafe { php_unregister_url_stream_wrapper_volatile((*name).as_ptr() as _) } {
0 => Ok(()),
_ => Err(Error::StreamWrapperUnregistrationFailure),
}
}
pub fn wops(&self) -> &php_stream_wrapper_ops {
unsafe { &*self.wops }
}
pub fn wops_mut(&mut self) -> &mut php_stream_wrapper_ops {
unsafe { &mut *(self.wops as *mut php_stream_wrapper_ops) }
}
}
pub type Stream = php_stream;
pub type StreamWrapperOps = php_stream_wrapper_ops;
impl StreamWrapperOps {}