From 78b950cc558afa4bbd2a2a33d116d3f8f437581e Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 7 Oct 2021 16:06:19 +1300 Subject: [PATCH 01/10] Refactor module layout --- build.rs | 8 +- src/{php => }/alloc.rs | 2 +- src/{php => }/args.rs | 16 +- src/{php/pack.rs => binary.rs} | 89 +- src/{php => }/boxed.rs | 0 src/{php => builders}/class.rs | 152 +-- src/{php => builders}/function.rs | 39 +- src/builders/mod.rs | 7 + src/{php => builders}/module.rs | 43 +- src/class.rs | 195 ++++ src/{php/types => }/closure.rs | 23 +- src/{php/constants.rs => constant.rs} | 4 +- src/convert.rs | 206 ++++ src/{errors.rs => error.rs} | 7 +- src/{php/exceptions.rs => exception.rs} | 77 +- src/{bindings.rs => ffi.rs} | 0 src/flags.rs | 363 +++++++ src/internal.rs | 9 + src/lib.rs | 60 +- src/macros.rs | 115 ++- src/php/enums.rs | 214 ---- src/php/flags.rs | 158 --- src/php/mod.rs | 16 - src/php/types/binary.rs | 88 -- src/php/types/long.rs | 9 - src/php/types/object.rs | 1074 --------------------- src/{php/types => }/props.rs | 8 +- src/{php/types => }/rc.rs | 7 +- src/{php => }/types/array.rs | 16 +- src/{php => }/types/callable.rs | 75 +- src/types/class_object.rs | 295 ++++++ src/types/long.rs | 70 ++ src/types/mod.rs | 32 + src/types/object.rs | 297 ++++++ src/{php => }/types/string.rs | 44 +- src/{php => }/types/zval.rs | 336 +------ src/{wrapper => }/wrapper.c | 0 src/{wrapper => }/wrapper.h | 0 src/{php/types/mod.rs => zend/_type.rs} | 27 +- src/zend/ce.rs | 65 ++ src/zend/class.rs | 121 +++ src/{php/execution_data.rs => zend/ex.rs} | 10 +- src/zend/function.rs | 26 + src/{php => zend}/globals.rs | 4 +- src/zend/handlers.rs | 257 +++++ src/zend/mod.rs | 16 + src/zend/module.rs | 13 + 47 files changed, 2358 insertions(+), 2335 deletions(-) rename src/{php => }/alloc.rs (96%) rename src/{php => }/args.rs (97%) rename src/{php/pack.rs => binary.rs} (61%) rename src/{php => }/boxed.rs (100%) rename src/{php => builders}/class.rs (67%) rename src/{php => builders}/function.rs (81%) create mode 100644 src/builders/mod.rs rename src/{php => builders}/module.rs (85%) create mode 100644 src/class.rs rename src/{php/types => }/closure.rs (96%) rename src/{php/constants.rs => constant.rs} (99%) create mode 100644 src/convert.rs rename src/{errors.rs => error.rs} (97%) rename src/{php/exceptions.rs => exception.rs} (61%) rename src/{bindings.rs => ffi.rs} (100%) create mode 100644 src/flags.rs create mode 100644 src/internal.rs delete mode 100644 src/php/enums.rs delete mode 100644 src/php/flags.rs delete mode 100644 src/php/mod.rs delete mode 100644 src/php/types/binary.rs delete mode 100644 src/php/types/long.rs delete mode 100644 src/php/types/object.rs rename src/{php/types => }/props.rs (98%) rename src/{php/types => }/rc.rs (93%) rename src/{php => }/types/array.rs (98%) rename src/{php => }/types/callable.rs (87%) create mode 100644 src/types/class_object.rs create mode 100644 src/types/long.rs create mode 100644 src/types/mod.rs create mode 100644 src/types/object.rs rename src/{php => }/types/string.rs (89%) rename src/{php => }/types/zval.rs (65%) rename src/{wrapper => }/wrapper.c (100%) rename src/{wrapper => }/wrapper.h (100%) rename src/{php/types/mod.rs => zend/_type.rs} (89%) create mode 100644 src/zend/ce.rs create mode 100644 src/zend/class.rs rename src/{php/execution_data.rs => zend/ex.rs} (96%) create mode 100644 src/zend/function.rs rename src/{php => zend}/globals.rs (92%) create mode 100644 src/zend/handlers.rs create mode 100644 src/zend/mod.rs create mode 100644 src/zend/module.rs diff --git a/build.rs b/build.rs index 4fbeed467f..ef84ec316c 100644 --- a/build.rs +++ b/build.rs @@ -12,8 +12,8 @@ const MAX_PHP_API_VER: u32 = 20200930; fn main() { // rerun if wrapper header is changed - println!("cargo:rerun-if-changed=src/wrapper/wrapper.h"); - println!("cargo:rerun-if-changed=src/wrapper/wrapper.c"); + println!("cargo:rerun-if-changed=src/wrapper.h"); + println!("cargo:rerun-if-changed=src/wrapper.c"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); @@ -71,7 +71,7 @@ fn main() { // Build `wrapper.c` and link to Rust. cc::Build::new() - .file("src/wrapper/wrapper.c") + .file("src/wrapper.c") .includes( str::replace(includes.as_ref(), "-I", "") .split(' ') @@ -80,7 +80,7 @@ fn main() { .compile("wrapper"); let mut bindgen = bindgen::Builder::default() - .header("src/wrapper/wrapper.h") + .header("src/wrapper.h") .clang_args(includes.split(' ')) .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .rustfmt_bindings(true) diff --git a/src/php/alloc.rs b/src/alloc.rs similarity index 96% rename from src/php/alloc.rs rename to src/alloc.rs index 6edaad9366..f425f82def 100644 --- a/src/php/alloc.rs +++ b/src/alloc.rs @@ -1,6 +1,6 @@ //! Functions relating to the Zend Memory Manager, used to allocate request-bound memory. -use crate::bindings::{_efree, _emalloc}; +use crate::ffi::{_efree, _emalloc}; use std::{alloc::Layout, ffi::c_void}; /// Uses the PHP memory allocator to allocate request-bound memory. diff --git a/src/php/args.rs b/src/args.rs similarity index 97% rename from src/php/args.rs rename to src/args.rs index e5d3ba683a..4920ff32ae 100644 --- a/src/php/args.rs +++ b/src/args.rs @@ -2,23 +2,19 @@ use std::{ffi::CString, ptr}; -use super::{ - enums::DataType, - types::{ - zval::{FromZvalMut, IntoZvalDyn, Zval}, - ZendType, - }, -}; - use crate::{ - bindings::{ + convert::{FromZvalMut, IntoZvalDyn}, + error::{Error, Result}, + ffi::{ _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, _zend_expected_type_Z_EXPECTED_BOOL, _zend_expected_type_Z_EXPECTED_DOUBLE, _zend_expected_type_Z_EXPECTED_LONG, _zend_expected_type_Z_EXPECTED_OBJECT, _zend_expected_type_Z_EXPECTED_RESOURCE, _zend_expected_type_Z_EXPECTED_STRING, zend_internal_arg_info, zend_wrong_parameters_count_error, }, - errors::{Error, Result}, + flags::DataType, + types::Zval, + zend::ZendType, }; /// Represents an argument to a function. diff --git a/src/php/pack.rs b/src/binary.rs similarity index 61% rename from src/php/pack.rs rename to src/binary.rs index 1572ac067b..0e72da26a9 100644 --- a/src/php/pack.rs +++ b/src/binary.rs @@ -4,7 +4,94 @@ //! [`pack`]: https://www.php.net/manual/en/function.pack.php //! [`unpack`]: https://www.php.net/manual/en/function.unpack.php -use crate::bindings::{ext_php_rs_zend_string_init, zend_string}; +use crate::ffi::{ext_php_rs_zend_string_init, zend_string}; + +use std::{ + convert::TryFrom, + iter::FromIterator, + ops::{Deref, DerefMut}, +}; + +use crate::{ + convert::{FromZval, IntoZval}, + error::{Error, Result}, + flags::DataType, + types::Zval, +}; + +/// Acts as a wrapper around [`Vec`] where `T` implements [`Pack`]. Primarily used for passing +/// binary data into Rust functions. Can be treated as a [`Vec`] in most situations, or can be +/// 'unwrapped' into a [`Vec`] through the [`From`] implementation on [`Vec`]. +#[derive(Debug)] +pub struct Binary(Vec); + +impl Binary { + /// Creates a new binary wrapper from a set of data which can be converted into a vector. + /// + /// # Parameters + /// + /// * `data` - Data to store inside the binary wrapper. + pub fn new(data: impl Into>) -> Self { + Self(data.into()) + } +} + +impl Deref for Binary { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Binary { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl FromZval<'_> for Binary { + const TYPE: DataType = DataType::String; + + fn from_zval(zval: &Zval) -> Option { + zval.binary().map(Binary) + } +} + +impl TryFrom for Binary { + type Error = Error; + + fn try_from(value: Zval) -> Result { + Self::from_zval(&value).ok_or_else(|| Error::ZvalConversion(value.get_type())) + } +} + +impl IntoZval for Binary { + const TYPE: DataType = DataType::String; + + fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { + zv.set_binary(self.0); + Ok(()) + } +} + +impl From> for Vec { + fn from(value: Binary) -> Self { + value.0 + } +} + +impl From> for Binary { + fn from(value: Vec) -> Self { + Self::new(value) + } +} + +impl FromIterator for Binary { + fn from_iter>(iter: U) -> Self { + Self(iter.into_iter().collect::>()) + } +} /// Used to convert between Zend binary strings and vectors. Useful in conjunction with the /// [`pack`] and [`unpack`] functions built-in to PHP. diff --git a/src/php/boxed.rs b/src/boxed.rs similarity index 100% rename from src/php/boxed.rs rename to src/boxed.rs diff --git a/src/php/class.rs b/src/builders/class.rs similarity index 67% rename from src/php/class.rs rename to src/builders/class.rs index 98e70bb0a0..455748b5a6 100644 --- a/src/php/class.rs +++ b/src/builders/class.rs @@ -1,131 +1,20 @@ -//! Builder and objects for creating classes in the PHP world. +use std::{alloc::Layout, ffi::CString}; use crate::{ - errors::{Error, Result}, - php::{ - exceptions::PhpException, - execution_data::ExecutionData, - function::FunctionBuilder, - types::object::{ConstructorMeta, ConstructorResult, ZendClassObject, ZendObject}, + builders::FunctionBuilder, + class::{ConstructorMeta, ConstructorResult, RegisteredClass}, + convert::IntoZval, + error::{Error, Result}, + exception::PhpException, + ffi::{ + zend_declare_class_constant, zend_declare_property, zend_do_implement_interface, + zend_register_internal_class_ex, }, -}; -use std::{alloc::Layout, convert::TryInto, ffi::CString, fmt::Debug, ops::DerefMut}; - -use crate::bindings::{ - zend_class_entry, zend_declare_class_constant, zend_declare_property, - zend_do_implement_interface, zend_register_internal_class_ex, -}; - -use super::{ flags::{ClassFlags, MethodFlags, PropertyFlags}, - function::FunctionEntry, - globals::ExecutorGlobals, - types::{ - object::RegisteredClass, - string::ZendStr, - zval::{IntoZval, Zval}, - }, + types::{ZendClassObject, ZendObject, ZendStr, Zval}, + zend::{ClassEntry, ExecutionData, FunctionEntry}, }; -/// A Zend class entry. Alias. -pub type ClassEntry = zend_class_entry; - -impl PartialEq for ClassEntry { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self, other) - } -} - -impl ClassEntry { - /// Attempts to find a reference to a class in the global class table. - /// - /// Returns a reference to the class if found, or [`None`] if the class could - /// not be found or the class table has not been initialized. - pub fn try_find(name: &str) -> Option<&'static Self> { - ExecutorGlobals::get().class_table()?; - let mut name = ZendStr::new(name, false).ok()?; - - unsafe { - crate::bindings::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0) - .as_ref() - } - } - - /// Returns the class flags. - pub fn flags(&self) -> ClassFlags { - ClassFlags::from_bits_truncate(self.ce_flags) - } - - /// Returns `true` if the class entry is an interface, and `false` otherwise. - pub fn is_interface(&self) -> bool { - self.flags().contains(ClassFlags::Interface) - } - - /// Checks if the class is an instance of another class or interface. - /// - /// # Parameters - /// - /// * `ce` - The inherited class entry to check. - pub fn instance_of(&self, ce: &ClassEntry) -> bool { - if self == ce { - return true; - } - - if ce.flags().contains(ClassFlags::Interface) { - let interfaces = match self.interfaces() { - Some(interfaces) => interfaces, - None => return false, - }; - - for i in interfaces { - if ce == i { - return true; - } - } - } else { - loop { - let parent = match self.parent() { - Some(parent) => parent, - None => return false, - }; - - if parent == ce { - return true; - } - } - } - - false - } - - /// Returns an iterator of all the interfaces that the class implements. Returns [`None`] if - /// the interfaces have not been resolved on the class. - pub fn interfaces(&self) -> Option> { - self.flags() - .contains(ClassFlags::ResolvedInterfaces) - .then(|| unsafe { - (0..self.num_interfaces) - .into_iter() - .map(move |i| *self.__bindgen_anon_3.interfaces.offset(i as _)) - .filter_map(|ptr| ptr.as_ref()) - }) - } - - /// Returns the parent of the class. - /// - /// If the parent of the class has not been resolved, it attempts to find the parent by name. - /// Returns [`None`] if the parent was not resolved and the parent was not able to be found - /// by name. - pub fn parent(&self) -> Option<&Self> { - if self.flags().contains(ClassFlags::ResolvedParent) { - unsafe { self.__bindgen_anon_1.parent.as_ref() } - } else { - let name = unsafe { self.__bindgen_anon_1.parent_name.as_ref()? }; - Self::try_find(name.as_str()?) - } - } -} - /// Builds a class to be exported as a PHP class. pub struct ClassBuilder { name: String, @@ -395,22 +284,3 @@ impl ClassBuilder { Ok(class) } } - -impl Debug for ClassEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name: String = unsafe { self.name.as_ref() } - .and_then(|s| s.try_into().ok()) - .ok_or(std::fmt::Error)?; - - f.debug_struct("ClassEntry") - .field("name", &name) - .field("flags", &self.flags()) - .field("is_interface", &self.is_interface()) - .field( - "interfaces", - &self.interfaces().map(|iter| iter.collect::>()), - ) - .field("parent", &self.parent()) - .finish() - } -} diff --git a/src/php/function.rs b/src/builders/function.rs similarity index 81% rename from src/php/function.rs rename to src/builders/function.rs index 9dd5d00853..d7d5b3e51e 100644 --- a/src/php/function.rs +++ b/src/builders/function.rs @@ -1,38 +1,11 @@ -//! Builder and objects used to create functions and methods in PHP. - -use std::{ffi::CString, mem, os::raw::c_char, ptr}; - -use crate::errors::Result; -use crate::{bindings::zend_function_entry, errors::Error}; - -use super::{ +use crate::{ args::{Arg, ArgInfo}, - enums::DataType, - execution_data::ExecutionData, - types::zval::Zval, - types::ZendType, + error::{Error, Result}, + flags::DataType, + types::Zval, + zend::{ExecutionData, FunctionEntry, ZendType}, }; - -/// A Zend function entry. Alias. -pub type FunctionEntry = zend_function_entry; - -impl FunctionEntry { - /// Returns an empty function entry, signifing the end of a function list. - pub fn end() -> Self { - Self { - fname: ptr::null() as *const c_char, - handler: None, - arg_info: ptr::null(), - num_args: 0, - flags: 0, - } - } - - /// Converts the function entry into a raw and pointer, releasing it to the C world. - pub fn into_raw(self) -> *mut Self { - Box::into_raw(Box::new(self)) - } -} +use std::{ffi::CString, mem, ptr}; /// Function representation in Rust. pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecutionData, retval: &mut Zval); diff --git a/src/builders/mod.rs b/src/builders/mod.rs new file mode 100644 index 0000000000..b803872b19 --- /dev/null +++ b/src/builders/mod.rs @@ -0,0 +1,7 @@ +mod class; +mod function; +mod module; + +pub use class::ClassBuilder; +pub use function::FunctionBuilder; +pub use module::ModuleBuilder; diff --git a/src/php/module.rs b/src/builders/module.rs similarity index 85% rename from src/php/module.rs rename to src/builders/module.rs index ae5c44a4d2..54e7c0b393 100644 --- a/src/php/module.rs +++ b/src/builders/module.rs @@ -1,26 +1,14 @@ -//! Builder and objects for creating modules in PHP. A module is the base of a PHP extension. +use crate::{ + error::Result, + ffi::{ext_php_rs_php_build_id, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO}, + zend::{FunctionEntry, ModuleEntry}, +}; use std::{ ffi::{c_void, CString}, mem, ptr, }; -use crate::{ - bindings::{ - ext_php_rs_php_build_id, zend_module_entry, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO, - }, - errors::Result, -}; - -use super::function::FunctionEntry; - -/// A Zend module entry. Alias. -pub type ModuleEntry = zend_module_entry; -/// A function to be called when the extension is starting up or shutting down. -pub type StartupShutdownFunc = extern "C" fn(_type: i32, _module_number: i32) -> i32; -/// A function to be called when `phpinfo();` is called. -pub type InfoFunc = extern "C" fn(zend_module: *mut ModuleEntry); - /// Builds a Zend extension. Must be called from within an external function called `get_module`, /// returning a mutable pointer to a `ModuleEntry`. /// @@ -172,20 +160,7 @@ impl ModuleBuilder { } } -impl ModuleEntry { - /// Converts the module entry into a raw pointer, releasing it to the C world. - pub fn into_raw(self) -> *mut Self { - Box::into_raw(Box::new(self)) - } -} - -/// Called by startup functions registered with the `#[php_startup]` macro. Initializes all -/// classes that are defined by ext-php-rs (i.e. [`Closure`]). -/// -/// [`Closure`]: ext_php_rs::php::types::closure::Closure -#[doc(hidden)] -#[inline(always)] -pub fn ext_php_rs_startup() { - #[cfg(feature = "closure")] - crate::php::types::closure::Closure::build(); -} +/// A function to be called when the extension is starting up or shutting down. +pub type StartupShutdownFunc = extern "C" fn(_type: i32, _module_number: i32) -> i32; +/// A function to be called when `phpinfo();` is called. +pub type InfoFunc = extern "C" fn(zend_module: *mut ModuleEntry); diff --git a/src/class.rs b/src/class.rs new file mode 100644 index 0000000000..a2540393e4 --- /dev/null +++ b/src/class.rs @@ -0,0 +1,195 @@ +use std::{ + collections::HashMap, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, AtomicPtr, Ordering}, +}; + +use crate::{ + builders::FunctionBuilder, + convert::{FromZval, IntoZval}, + exception::PhpException, + props::Property, + types::ZendClassObject, + zend::{ClassEntry, ExecutionData, ZendObjectHandlers}, +}; + +/// Implemented on Rust types which are exported to PHP. Allows users to get and set PHP properties on +/// the object. +pub trait RegisteredClass: Sized +where + Self: 'static, +{ + /// PHP class name of the registered class. + const CLASS_NAME: &'static str; + + /// Optional class constructor. + const CONSTRUCTOR: Option> = None; + + /// Returns a reference to the class metadata, which stores the class entry and handlers. + /// + /// This must be statically allocated, and is usually done through the [`macro@php_class`] + /// macro. + /// + /// [`macro@php_class`]: crate::php_class + fn get_metadata() -> &'static ClassMetadata; + + /// Attempts to retrieve a property from the class object. + /// + /// # Parameters + /// + /// * `name` - The name of the property. + /// + /// # Returns + /// + /// Returns a given type `T` inside an option which is the value of the zval, or [`None`] + /// if the property could not be found. + /// + /// # Safety + /// + /// Caller must guarantee that the object the function is called on is immediately followed + /// by a [`zend_object`], which is true when the object was instantiated by PHP. + unsafe fn get_property<'a, T: FromZval<'a>>(&'a self, name: &str) -> Option { + let obj = ZendClassObject::::from_obj_ptr(self)?; + obj.std.get_property(name).ok() + } + + /// Attempts to set the value of a property on the class object. + /// + /// # Parameters + /// + /// * `name` - The name of the property to set. + /// * `value` - The value to set the property to. + /// + /// # Returns + /// + /// Returns nothing in an option if the property was successfully set. Returns none if setting + /// the value failed. + /// + /// # Safety + /// + /// Caller must guarantee that the object the function is called on is immediately followed + /// by a [`zend_object`], which is true when the object was instantiated by PHP. + unsafe fn set_property(&mut self, name: &str, value: impl IntoZval) -> Option<()> { + let obj = ZendClassObject::::from_obj_ptr(self)?; + obj.std.set_property(name, value).ok()?; + Some(()) + } + + /// Returns a hash table containing the properties of the class. + /// + /// The key should be the name of the property and the value should be a reference to the property + /// with reference to `self`. The value is a [`Property`]. + fn get_properties<'a>() -> HashMap<&'static str, Property<'a, Self>>; +} + +/// Object constructor metadata. +pub struct ConstructorMeta { + /// Constructor function. + pub constructor: fn(&mut ExecutionData) -> ConstructorResult, + /// Function called to build the constructor function. Usually adds arguments. + pub build_fn: fn(FunctionBuilder) -> FunctionBuilder, +} + +/// Result returned from a constructor of a class. +pub enum ConstructorResult { + /// Successfully constructed the class, contains the new class object. + Ok(T), + /// An exception occured while constructing the class. + Exception(PhpException), + /// Invalid arguments were given to the constructor. + ArgError, +} + +impl From> for ConstructorResult +where + E: Into, +{ + fn from(result: std::result::Result) -> Self { + match result { + Ok(x) => Self::Ok(x), + Err(e) => Self::Exception(e.into()), + } + } +} + +impl From for ConstructorResult { + fn from(result: T) -> Self { + Self::Ok(result) + } +} + +/// Stores the class entry and handlers for a Rust type which has been exported to PHP. +pub struct ClassMetadata { + handlers_init: AtomicBool, + handlers: MaybeUninit, + ce: AtomicPtr, + + phantom: PhantomData, +} + +impl ClassMetadata { + /// Creates a new class metadata instance. + pub const fn new() -> Self { + Self { + handlers_init: AtomicBool::new(false), + handlers: MaybeUninit::uninit(), + ce: AtomicPtr::new(std::ptr::null_mut()), + phantom: PhantomData, + } + } +} + +impl ClassMetadata { + /// Returns an immutable reference to the object handlers contained inside the class metadata. + pub fn handlers(&self) -> &ZendObjectHandlers { + self.check_handlers(); + + // SAFETY: `check_handlers` guarantees that `handlers` has been initialized. + unsafe { &*self.handlers.as_ptr() } + } + + /// Checks if the class entry has been stored, returning a boolean. + pub fn has_ce(&self) -> bool { + !self.ce.load(Ordering::SeqCst).is_null() + } + + /// Retrieves a reference to the stored class entry. + /// + /// # Panics + /// + /// Panics if there is no class entry stored inside the class metadata. + pub fn ce(&self) -> &'static ClassEntry { + // SAFETY: There are only two values that can be stored in the atomic ptr: null or a static reference + // to a class entry. On the latter case, `as_ref()` will return `None` and the function will panic. + unsafe { self.ce.load(Ordering::SeqCst).as_ref() } + .expect("Attempted to retrieve class entry before it has been stored.") + } + + /// Stores a reference to a class entry inside the class metadata. + /// + /// # Parameters + /// + /// * `ce` - The class entry to store. + /// + /// # Panics + /// + /// Panics if the class entry has already been set in the class metadata. This function should + /// only be called once. + pub fn set_ce(&self, ce: &'static mut ClassEntry) { + if !self.ce.load(Ordering::SeqCst).is_null() { + panic!("Class entry has already been set."); + } + + self.ce.store(ce, Ordering::SeqCst); + } + + /// Checks if the handlers have been initialized, and initializes them if they are not. + fn check_handlers(&self) { + if !self.handlers_init.load(Ordering::Acquire) { + // SAFETY: `MaybeUninit` has the same size as the handlers. + unsafe { ZendObjectHandlers::init::(self.handlers.as_ptr() as *mut _) }; + self.handlers_init.store(true, Ordering::Release); + } + } +} diff --git a/src/php/types/closure.rs b/src/closure.rs similarity index 96% rename from src/php/types/closure.rs rename to src/closure.rs index 72c3a74535..49120a3c4f 100644 --- a/src/php/types/closure.rs +++ b/src/closure.rs @@ -2,21 +2,16 @@ use std::collections::HashMap; -use crate::php::{ +use crate::{ args::{Arg, ArgParser}, - class::ClassBuilder, - enums::DataType, - exceptions::PhpException, - execution_data::ExecutionData, - flags::MethodFlags, - function::FunctionBuilder, - types::object::ClassMetadata, -}; - -use super::{ - object::RegisteredClass, + builders::{ClassBuilder, FunctionBuilder}, + class::{ClassMetadata, RegisteredClass}, + convert::{FromZval, IntoZval}, + exception::PhpException, + flags::{DataType, MethodFlags}, props::Property, - zval::{FromZval, IntoZval, Zval}, + types::Zval, + zend::ExecutionData, }; /// Class entry and handlers for Rust closures. @@ -147,7 +142,7 @@ impl Closure { impl RegisteredClass for Closure { const CLASS_NAME: &'static str = "RustClosure"; - fn get_metadata() -> &'static super::object::ClassMetadata { + fn get_metadata() -> &'static ClassMetadata { &CLOSURE_META } diff --git a/src/php/constants.rs b/src/constant.rs similarity index 99% rename from src/php/constants.rs rename to src/constant.rs index 9a4d167120..6d7a3577b2 100644 --- a/src/php/constants.rs +++ b/src/constant.rs @@ -3,11 +3,11 @@ use std::ffi::CString; use super::flags::GlobalConstantFlags; -use crate::bindings::{ +use crate::error::Result; +use crate::ffi::{ zend_register_bool_constant, zend_register_double_constant, zend_register_long_constant, zend_register_string_constant, }; -use crate::errors::Result; pub trait IntoConst: Sized { /// Registers a global module constant in PHP, with the value as the content of self. diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000000..33d92af8b4 --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,206 @@ +use crate::{ + boxed::ZBox, + error::Result, + exception::PhpException, + flags::DataType, + types::{ZendObject, Zval}, +}; + +/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal of the [`IntoZval`] +/// trait. +pub trait FromZval<'a>: Sized { + /// The corresponding type of the implemented value in PHP. + const TYPE: DataType; + + /// Attempts to retrieve an instance of `Self` from a reference to a [`Zval`]. + /// + /// # Parameters + /// + /// * `zval` - Zval to get value from. + fn from_zval(zval: &'a Zval) -> Option; +} + +impl<'a, T> FromZval<'a> for Option +where + T: FromZval<'a>, +{ + const TYPE: DataType = T::TYPE; + + fn from_zval(zval: &'a Zval) -> Option { + Some(T::from_zval(zval)) + } +} + +/// Allows mutable zvals to be converted into Rust types in a fallible way. +/// +/// If `Self` does not require the zval to be mutable to be extracted, you should implement +/// [`FromZval`] instead, as this trait is generically implemented for any type that implements +/// [`FromZval`]. +pub trait FromZvalMut<'a>: Sized { + /// The corresponding type of the implemented value in PHP. + const TYPE: DataType; + + /// Attempts to retrieve an instance of `Self` from a mutable reference to a [`Zval`]. + /// + /// # Parameters + /// + /// * `zval` - Zval to get value from. + fn from_zval_mut(zval: &'a mut Zval) -> Option; +} + +impl<'a, T> FromZvalMut<'a> for T +where + T: FromZval<'a>, +{ + const TYPE: DataType = ::TYPE; + + #[inline] + fn from_zval_mut(zval: &'a mut Zval) -> Option { + Self::from_zval(zval) + } +} + +/// `FromZendObject` is implemented by types which can be extracted from a Zend object. +/// +/// Normal usage is through the helper method `ZendObject::extract`: +/// +/// ```rust,ignore +/// let obj: ZendObject = ...; +/// let repr: String = obj.extract(); +/// let props: HashMap = obj.extract(); +/// ``` +/// +/// Should be functionally equivalent to casting an object to another compatable type. +pub trait FromZendObject<'a>: Sized { + /// Extracts `Self` from the source `ZendObject`. + fn from_zend_object(obj: &'a ZendObject) -> Result; +} + +/// Implemented on types which can be extracted from a mutable zend object. +/// +/// If `Self` does not require the object to be mutable, it should implement +/// [`FromZendObject`] instead, as this trait is generically implemented for +/// any types that also implement [`FromZendObject`]. +pub trait FromZendObjectMut<'a>: Sized { + /// Extracts `Self` from the source `ZendObject`. + fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result; +} + +impl<'a, T> FromZendObjectMut<'a> for T +where + T: FromZendObject<'a>, +{ + #[inline] + fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result { + Self::from_zend_object(obj) + } +} + +/// Implemented on types which can be converted into a Zend object. It is up to the implementation +/// to determine the type of object which is produced. +pub trait IntoZendObject { + /// Attempts to convert `self` into a Zend object. + fn into_zend_object(self) -> Result>; +} + +/// Provides implementations for converting Rust primitive types into PHP zvals. Alternative to the +/// built-in Rust [`From`] and [`TryFrom`] implementations, allowing the caller to specify whether +/// the Zval contents will persist between requests. +pub trait IntoZval: Sized { + /// The corresponding type of the implemented value in PHP. + const TYPE: DataType; + + /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if + /// successful. + /// + /// # Parameters + /// + /// * `persistent` - Whether the contents of the Zval will persist between requests. + fn into_zval(self, persistent: bool) -> Result { + let mut zval = Zval::new(); + self.set_zval(&mut zval, persistent)?; + Ok(zval) + } + + /// Sets the content of a pre-existing zval. Returns a result containing nothing if setting + /// the content was successful. + /// + /// # Parameters + /// + /// * `zv` - The Zval to set the content of. + /// * `persistent` - Whether the contents of the Zval will persist between requests. + fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()>; +} + +impl IntoZval for () { + const TYPE: DataType = DataType::Void; + + #[inline] + fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { + zv.set_null(); + Ok(()) + } +} + +impl IntoZval for Option +where + T: IntoZval, +{ + const TYPE: DataType = T::TYPE; + + #[inline] + fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { + match self { + Some(val) => val.set_zval(zv, persistent), + None => { + zv.set_null(); + Ok(()) + } + } + } +} + +impl IntoZval for std::result::Result +where + T: IntoZval, + E: Into, +{ + const TYPE: DataType = T::TYPE; + + fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { + match self { + Ok(val) => val.set_zval(zv, persistent), + Err(e) => { + let ex: PhpException = e.into(); + ex.throw() + } + } + } +} + +/// An object-safe version of the [`IntoZval`] trait. +/// +/// This trait is automatically implemented on any type that implements both [`IntoZval`] and [`Clone`]. +/// You avoid implementing this trait directly, rather implement these two other traits. +pub trait IntoZvalDyn { + /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if + /// successful. `self` is cloned before being converted into a zval. + /// + /// # Parameters + /// + /// * `persistent` - Whether the contents of the Zval will persist between requests. + fn as_zval(&self, persistent: bool) -> Result; + + /// Returns the PHP type of the type. + fn get_type(&self) -> DataType; +} + +impl IntoZvalDyn for T { + fn as_zval(&self, persistent: bool) -> Result { + self.clone().into_zval(persistent) + } + + fn get_type(&self) -> DataType { + Self::TYPE + } +} diff --git a/src/errors.rs b/src/error.rs similarity index 97% rename from src/errors.rs rename to src/error.rs index 72eccca7ca..41b296288e 100644 --- a/src/errors.rs +++ b/src/error.rs @@ -2,10 +2,9 @@ use std::{error::Error as ErrorTrait, ffi::NulError, fmt::Display}; -use crate::php::{ - enums::DataType, - exceptions::PhpException, - flags::{ClassFlags, ZvalTypeFlags}, +use crate::{ + exception::PhpException, + flags::{ClassFlags, DataType, ZvalTypeFlags}, }; /// The main result type which is passed by the library. diff --git a/src/php/exceptions.rs b/src/exception.rs similarity index 61% rename from src/php/exceptions.rs rename to src/exception.rs index 87ce1b3cd1..9265e88906 100644 --- a/src/php/exceptions.rs +++ b/src/exception.rs @@ -2,16 +2,12 @@ use std::ffi::CString; -use super::{class::ClassEntry, types::object::RegisteredClass}; use crate::{ - bindings::{ - zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_compile_error, - zend_ce_division_by_zero_error, zend_ce_error_exception, zend_ce_exception, - zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error, - zend_ce_value_error, zend_throw_exception_ex, - }, - errors::{Error, Result}, - php::flags::ClassFlags, + class::RegisteredClass, + error::{Error, Result}, + ffi::zend_throw_exception_ex, + flags::ClassFlags, + zend::{ce, ClassEntry}, }; /// Result type with the error variant as a [`PhpException`]. @@ -50,7 +46,7 @@ impl PhpException { /// /// * `message` - Message to contain in the exception. pub fn default(message: String) -> Self { - Self::new(message, 0, ClassEntry::exception()) + Self::new(message, 0, ce::exception()) } /// Creates an instance of an exception from a PHP class type and a message. @@ -140,64 +136,3 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> }; Ok(()) } - -// SAFETY: All default exceptions have been initialized by the time we should use these (in the module -// startup function). Note that they are not valid during the module init function, but rather than -// wrapping everything -#[allow(clippy::unwrap_used)] -impl ClassEntry { - /// Returns the base `Throwable` class. - pub fn throwable() -> &'static Self { - unsafe { zend_ce_throwable.as_ref() }.unwrap() - } - - /// Returns the base `Exception` class. - pub fn exception() -> &'static Self { - unsafe { zend_ce_exception.as_ref() }.unwrap() - } - - /// Returns the base `ErrorException` class. - pub fn error_exception() -> &'static Self { - unsafe { zend_ce_error_exception.as_ref() }.unwrap() - } - - /// Returns the base `CompileError` class. - pub fn compile_error() -> &'static Self { - unsafe { zend_ce_compile_error.as_ref() }.unwrap() - } - - /// Returns the base `ParseError` class. - pub fn parse_error() -> &'static Self { - unsafe { zend_ce_parse_error.as_ref() }.unwrap() - } - - /// Returns the base `TypeError` class. - pub fn type_error() -> &'static Self { - unsafe { zend_ce_type_error.as_ref() }.unwrap() - } - - /// Returns the base `ArgumentCountError` class. - pub fn argument_count_error() -> &'static Self { - unsafe { zend_ce_argument_count_error.as_ref() }.unwrap() - } - - /// Returns the base `ValueError` class. - pub fn value_error() -> &'static Self { - unsafe { zend_ce_value_error.as_ref() }.unwrap() - } - - /// Returns the base `ArithmeticError` class. - pub fn arithmetic_error() -> &'static Self { - unsafe { zend_ce_arithmetic_error.as_ref() }.unwrap() - } - - /// Returns the base `DivisionByZeroError` class. - pub fn division_by_zero_error() -> &'static Self { - unsafe { zend_ce_division_by_zero_error.as_ref() }.unwrap() - } - - /// Returns the base `UnhandledMatchError` class. - pub fn unhandled_match_error() -> &'static Self { - unsafe { zend_ce_unhandled_match_error.as_ref() }.unwrap() - } -} diff --git a/src/bindings.rs b/src/ffi.rs similarity index 100% rename from src/bindings.rs rename to src/ffi.rs diff --git a/src/flags.rs b/src/flags.rs new file mode 100644 index 0000000000..e197b2084c --- /dev/null +++ b/src/flags.rs @@ -0,0 +1,363 @@ +//! Bitflags used in PHP and the Zend engine. + +use bitflags::bitflags; + +use crate::ffi::{ + CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, IS_ARRAY, IS_CALLABLE, + IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, + IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, + IS_UNDEF, IS_VOID, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, + ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, + ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, + ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, + ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE, + ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, + ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, + ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED, + ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT, + ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, + ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, + ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS, + Z_TYPE_FLAGS_SHIFT, _IS_BOOL, +}; + +use std::{convert::TryFrom, fmt::Display}; + +use crate::error::{Error, Result}; + +bitflags! { + /// Flags used for setting the type of Zval. + pub struct ZvalTypeFlags: u32 { + const Undef = IS_UNDEF; + const Null = IS_NULL; + const False = IS_FALSE; + const True = IS_TRUE; + const Long = IS_LONG; + const Double = IS_DOUBLE; + const String = IS_STRING; + const Array = IS_ARRAY; + const Object = IS_OBJECT; + const Resource = IS_RESOURCE; + const Reference = IS_REFERENCE; + const Callable = IS_CALLABLE; + const ConstantExpression = IS_CONSTANT_AST; + const Void = IS_VOID; + const Ptr = IS_PTR; + + const InternedStringEx = Self::String.bits; + const StringEx = Self::String.bits | Self::RefCounted.bits; + const ArrayEx = Self::Array.bits | Self::RefCounted.bits | Self::Collectable.bits; + const ObjectEx = Self::Object.bits | Self::RefCounted.bits | Self::Collectable.bits; + const ResourceEx = Self::Resource.bits | Self::RefCounted.bits; + const ReferenceEx = Self::Reference.bits | Self::RefCounted.bits; + const ConstantAstEx = Self::ConstantExpression.bits | Self::RefCounted.bits; + + const RefCounted = (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT); + const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT); + } +} + +bitflags! { + /// Flags for building classes. + pub struct ClassFlags: u32 { + const Final = ZEND_ACC_FINAL; + const Abstract = ZEND_ACC_ABSTRACT; + const Immutable = ZEND_ACC_IMMUTABLE; + const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS; + const TopLevel = ZEND_ACC_TOP_LEVEL; + const Preloaded = ZEND_ACC_PRELOADED; + + const Interface = ZEND_ACC_INTERFACE; + const Trait = ZEND_ACC_TRAIT; + const AnonymousClass = ZEND_ACC_ANON_CLASS; + const Linked = ZEND_ACC_LINKED; + const ImplicitAbstractClass = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; + const UseGuards = ZEND_ACC_USE_GUARDS; + const ConstantsUpdated = ZEND_ACC_CONSTANTS_UPDATED; + const NoDynamicProperties = ZEND_ACC_NO_DYNAMIC_PROPERTIES; + const HasStaticInMethods = ZEND_HAS_STATIC_IN_METHODS; + const PropertyTypesResolved = ZEND_ACC_PROPERTY_TYPES_RESOLVED; + const ReuseGetIterator = ZEND_ACC_REUSE_GET_ITERATOR; + const ResolvedParent = ZEND_ACC_RESOLVED_PARENT; + const ResolvedInterfaces = ZEND_ACC_RESOLVED_INTERFACES; + const UnresolvedVariance = ZEND_ACC_UNRESOLVED_VARIANCE; + const NearlyLinked = ZEND_ACC_NEARLY_LINKED; + const HasUnlinkedUses = ZEND_ACC_HAS_UNLINKED_USES; + } +} + +bitflags! { + /// Flags for building methods. + pub struct MethodFlags: u32 { + const Public = ZEND_ACC_PUBLIC; + const Protected = ZEND_ACC_PROTECTED; + const Private = ZEND_ACC_PRIVATE; + const Changed = ZEND_ACC_CHANGED; + const Static = ZEND_ACC_STATIC; + const Final = ZEND_ACC_FINAL; + const Abstract = ZEND_ACC_ABSTRACT; + const Immutable = ZEND_ACC_IMMUTABLE; + const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS; + const TopLevel = ZEND_ACC_TOP_LEVEL; + const Preloaded = ZEND_ACC_PRELOADED; + + const Deprecated = ZEND_ACC_DEPRECATED; + const ReturnReference = ZEND_ACC_RETURN_REFERENCE; + const HasReturnType = ZEND_ACC_HAS_RETURN_TYPE; + const Variadic = ZEND_ACC_VARIADIC; + const HasFinallyBlock = ZEND_ACC_HAS_FINALLY_BLOCK; + const EarlyBinding = ZEND_ACC_EARLY_BINDING; + const UsesThis = ZEND_ACC_USES_THIS; + const CallViaTrampoline = ZEND_ACC_CALL_VIA_TRAMPOLINE; + const NeverCache = ZEND_ACC_NEVER_CACHE; + const TraitClone = ZEND_ACC_TRAIT_CLONE; + const IsConstructor = ZEND_ACC_CTOR; + const Closure = ZEND_ACC_CLOSURE; + const FakeClosure = ZEND_ACC_FAKE_CLOSURE; + const Generator = ZEND_ACC_GENERATOR; + const DonePassTwo = ZEND_ACC_DONE_PASS_TWO; + const HeapRTCache = ZEND_ACC_HEAP_RT_CACHE; + const StrictTypes = ZEND_ACC_STRICT_TYPES; + } +} + +bitflags! { + /// Flags for building properties. + pub struct PropertyFlags: u32 { + const Public = ZEND_ACC_PUBLIC; + const Protected = ZEND_ACC_PROTECTED; + const Private = ZEND_ACC_PRIVATE; + const Changed = ZEND_ACC_CHANGED; + const Static = ZEND_ACC_STATIC; + const Promoted = ZEND_ACC_PROMOTED; + } +} + +bitflags! { + /// Flags for building constants. + pub struct ConstantFlags: u32 { + const Public = ZEND_ACC_PUBLIC; + const Protected = ZEND_ACC_PROTECTED; + const Private = ZEND_ACC_PRIVATE; + const Promoted = ZEND_ACC_PROMOTED; + } +} + +bitflags! { + /// Flags for building module global constants. + pub struct GlobalConstantFlags: u32 { + const CaseSensitive = CONST_CS; + const Persistent = CONST_PERSISTENT; + const NoFileCache = CONST_NO_FILE_CACHE; + const Deprecated = CONST_DEPRECATED; + } +} + +bitflags! { + /// Represents the result of a function. + pub struct ZendResult: i32 { + const Success = 0; + const Failure = -1; + } +} + +/// Valid data types for PHP. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DataType { + Undef, + Null, + False, + True, + Long, + Double, + String, + Array, + Object(Option<&'static str>), + Resource, + Reference, + Callable, + ConstantExpression, + Void, + Mixed, + Bool, + Ptr, +} + +impl Default for DataType { + fn default() -> Self { + Self::Void + } +} + +impl DataType { + /// Returns the integer representation of the data type. + pub const fn as_u32(&self) -> u32 { + match self { + DataType::Undef => IS_UNDEF, + DataType::Null => IS_NULL, + DataType::False => IS_FALSE, + DataType::True => IS_TRUE, + DataType::Long => IS_LONG, + DataType::Double => IS_DOUBLE, + DataType::String => IS_STRING, + DataType::Array => IS_ARRAY, + DataType::Object(_) => IS_OBJECT, + DataType::Resource => IS_RESOURCE, + DataType::Reference => IS_RESOURCE, + DataType::Callable => IS_CALLABLE, + DataType::ConstantExpression => IS_CONSTANT_AST, + DataType::Void => IS_VOID, + DataType::Mixed => IS_MIXED, + DataType::Bool => _IS_BOOL, + DataType::Ptr => IS_PTR, + } + } +} + +// TODO: Ideally want something like this +// pub struct Type { +// data_type: DataType, +// is_refcounted: bool, +// is_collectable: bool, +// is_immutable: bool, +// is_persistent: bool, +// } +// +// impl From for Type { ... } + +impl TryFrom for DataType { + type Error = Error; + + fn try_from(value: ZvalTypeFlags) -> Result { + macro_rules! contains { + ($t: ident) => { + if value.contains(ZvalTypeFlags::$t) { + return Ok(DataType::$t); + } + }; + } + + contains!(Undef); + contains!(Null); + contains!(False); + contains!(True); + contains!(False); + contains!(Long); + contains!(Double); + contains!(String); + contains!(Array); + contains!(Resource); + contains!(Callable); + contains!(ConstantExpression); + contains!(Void); + + if value.contains(ZvalTypeFlags::Object) { + return Ok(DataType::Object(None)); + } + + Err(Error::UnknownDatatype(0)) + } +} + +impl From for DataType { + #[allow(clippy::bad_bit_mask)] + fn from(value: u32) -> Self { + macro_rules! contains { + ($c: ident, $t: ident) => { + if (value & $c) == $c { + return DataType::$t; + } + }; + } + + contains!(IS_VOID, Void); + contains!(IS_CALLABLE, Callable); + contains!(IS_CONSTANT_AST, ConstantExpression); + contains!(IS_REFERENCE, Reference); + contains!(IS_RESOURCE, Resource); + contains!(IS_ARRAY, Array); + contains!(IS_STRING, String); + contains!(IS_DOUBLE, Double); + contains!(IS_LONG, Long); + contains!(IS_TRUE, True); + contains!(IS_FALSE, False); + contains!(IS_NULL, Null); + contains!(IS_PTR, Ptr); + + if (value & IS_OBJECT) == IS_OBJECT { + return DataType::Object(None); + } + + contains!(IS_UNDEF, Undef); + + DataType::Mixed + } +} + +impl Display for DataType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DataType::Undef => write!(f, "Undefined"), + DataType::Null => write!(f, "Null"), + DataType::False => write!(f, "False"), + DataType::True => write!(f, "True"), + DataType::Long => write!(f, "Long"), + DataType::Double => write!(f, "Double"), + DataType::String => write!(f, "String"), + DataType::Array => write!(f, "Array"), + DataType::Object(obj) => write!(f, "{}", obj.as_deref().unwrap_or("Object")), + DataType::Resource => write!(f, "Resource"), + DataType::Reference => write!(f, "Reference"), + DataType::Callable => write!(f, "Callable"), + DataType::ConstantExpression => write!(f, "Constant Expression"), + DataType::Void => write!(f, "Void"), + DataType::Bool => write!(f, "Bool"), + DataType::Mixed => write!(f, "Mixed"), + DataType::Ptr => write!(f, "Pointer"), + } + } +} + +#[cfg(test)] +mod tests { + use super::DataType; + use crate::ffi::{ + IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, + IS_FALSE, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_REFERENCE, + IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, IS_TRUE, IS_UNDEF, + IS_VOID, + }; + use std::convert::TryFrom; + + #[test] + fn test_datatype() { + macro_rules! test { + ($c: ident, $t: ident) => { + assert_eq!(DataType::try_from($c), Ok(DataType::$t)); + }; + } + + test!(IS_UNDEF, Undef); + test!(IS_NULL, Null); + test!(IS_FALSE, False); + test!(IS_TRUE, True); + test!(IS_LONG, Long); + test!(IS_DOUBLE, Double); + test!(IS_STRING, String); + test!(IS_ARRAY, Array); + assert_eq!(DataType::try_from(IS_OBJECT), Ok(DataType::Object(None))); + test!(IS_RESOURCE, Resource); + test!(IS_REFERENCE, Reference); + test!(IS_CONSTANT_AST, ConstantExpression); + test!(IS_CALLABLE, Callable); + test!(IS_VOID, Void); + + test!(IS_INTERNED_STRING_EX, String); + test!(IS_STRING_EX, String); + test!(IS_ARRAY_EX, Array); + assert_eq!(DataType::try_from(IS_OBJECT_EX), Ok(DataType::Object(None))); + test!(IS_RESOURCE_EX, Resource); + test!(IS_REFERENCE_EX, Reference); + test!(IS_CONSTANT_AST_EX, ConstantExpression); + } +} diff --git a/src/internal.rs b/src/internal.rs new file mode 100644 index 0000000000..a395fddfdd --- /dev/null +++ b/src/internal.rs @@ -0,0 +1,9 @@ +/// Called by startup functions registered with the `#[php_startup]` macro. Initializes all +/// classes that are defined by ext-php-rs (i.e. [`Closure`]). +/// +/// [`Closure`]: ext_php_rs::php::types::closure::Closure +#[inline(always)] +pub fn ext_php_rs_startup() { + #[cfg(feature = "closure")] + crate::closure::Closure::build(); +} diff --git a/src/lib.rs b/src/lib.rs index 2b1aea2404..e9f58232b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,47 @@ #![allow(non_snake_case)] #![cfg_attr(docs, feature(doc_cfg))] +pub mod alloc; +pub mod args; +pub mod binary; +pub mod builders; +pub mod convert; +pub mod error; +pub mod exception; +pub mod ffi; +pub mod flags; #[macro_use] pub mod macros; -pub mod bindings; -pub mod errors; -pub mod php; +pub mod boxed; +pub mod class; +#[cfg(any(docs, feature = "closure"))] +#[cfg_attr(docs, doc(cfg(feature = "closure")))] +pub mod closure; +pub mod constant; +#[doc(hidden)] +pub mod internal; +pub mod props; +pub mod rc; +pub mod types; +pub mod zend; + +/// A module typically glob-imported containing the typically required macros and imports. +pub mod prelude { + pub use crate::builders::ModuleBuilder; + #[cfg(any(docs, feature = "closure"))] + #[cfg_attr(docs, doc(cfg(feature = "closure")))] + pub use crate::closure::Closure; + pub use crate::exception::{PhpException, PhpResult}; + pub use crate::php_class; + pub use crate::php_const; + pub use crate::php_extern; + pub use crate::php_function; + pub use crate::php_impl; + pub use crate::php_module; + pub use crate::php_startup; + pub use crate::types::Callable; + pub use crate::ZvalConvert; +} /// Attribute used to annotate constants to be exported to PHP. /// @@ -571,21 +607,3 @@ pub use ext_php_rs_derive::php_startup; /// [`Zval`]: crate::php::types::zval::Zval /// [`Zval::string`]: crate::php::types::zval::Zval::string pub use ext_php_rs_derive::ZvalConvert; - -/// A module typically glob-imported containing the typically required macros and imports. -pub mod prelude { - pub use crate::php::exceptions::{PhpException, PhpResult}; - pub use crate::php::module::ModuleBuilder; - pub use crate::php::types::callable::Callable; - #[cfg(any(docs, feature = "closure"))] - #[cfg_attr(docs, doc(cfg(feature = "closure")))] - pub use crate::php::types::closure::Closure; - pub use crate::php_class; - pub use crate::php_const; - pub use crate::php_extern; - pub use crate::php_function; - pub use crate::php_impl; - pub use crate::php_module; - pub use crate::php_startup; - pub use crate::ZvalConvert; -} diff --git a/src/macros.rs b/src/macros.rs index 168f74cbbb..118d14cf3c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -223,85 +223,124 @@ macro_rules! throw { #[macro_export] macro_rules! class_derives { ($type: ty) => { - impl<'a> $crate::php::types::object::FromZendObject<'a> for &'a $type { + impl<'a> $crate::convert::FromZendObject<'a> for &'a $type { #[inline] - fn from_zend_object( - obj: &'a $crate::php::types::object::ZendObject, - ) -> $crate::errors::Result { - let obj = $crate::php::types::object::ZendClassObject::<$type>::from_zend_obj(obj) - .ok_or($crate::errors::Error::InvalidScope)?; + fn from_zend_object(obj: &'a $crate::types::ZendObject) -> $crate::error::Result { + let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj(obj) + .ok_or($crate::error::Error::InvalidScope)?; Ok(&**obj) } } - impl<'a> $crate::php::types::object::FromZendObjectMut<'a> for &'a mut $type { + impl<'a> $crate::convert::FromZendObjectMut<'a> for &'a mut $type { #[inline] fn from_zend_object_mut( - obj: &'a mut $crate::php::types::object::ZendObject, - ) -> $crate::errors::Result { - let obj = - $crate::php::types::object::ZendClassObject::<$type>::from_zend_obj_mut(obj) - .ok_or($crate::errors::Error::InvalidScope)?; + obj: &'a mut $crate::types::ZendObject, + ) -> $crate::error::Result { + let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj_mut(obj) + .ok_or($crate::error::Error::InvalidScope)?; Ok(&mut **obj) } } - impl<'a> $crate::php::types::zval::FromZval<'a> for &'a $type { - const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some( - <$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME, + impl<'a> $crate::convert::FromZval<'a> for &'a $type { + const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some( + <$type as $crate::class::RegisteredClass>::CLASS_NAME, )); #[inline] - fn from_zval(zval: &'a $crate::php::types::zval::Zval) -> ::std::option::Option { - ::from_zend_object( - zval.object()?, - ) - .ok() + fn from_zval(zval: &'a $crate::types::Zval) -> ::std::option::Option { + ::from_zend_object(zval.object()?).ok() } } - impl<'a> $crate::php::types::zval::FromZvalMut<'a> for &'a mut $type { - const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some( - <$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME, + impl<'a> $crate::convert::FromZvalMut<'a> for &'a mut $type { + const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some( + <$type as $crate::class::RegisteredClass>::CLASS_NAME, )); #[inline] - fn from_zval_mut( - zval: &'a mut $crate::php::types::zval::Zval, - ) -> ::std::option::Option { - ::from_zend_object_mut( + fn from_zval_mut(zval: &'a mut $crate::types::Zval) -> ::std::option::Option { + ::from_zend_object_mut( zval.object_mut()?, ) .ok() } } - impl $crate::php::types::object::IntoZendObject for $type { + impl $crate::convert::IntoZendObject for $type { #[inline] fn into_zend_object( self, - ) -> $crate::errors::Result< - $crate::php::boxed::ZBox<$crate::php::types::object::ZendObject>, - > { - Ok($crate::php::types::object::ZendClassObject::new(self).into()) + ) -> $crate::error::Result<$crate::boxed::ZBox<$crate::types::ZendObject>> { + Ok($crate::types::ZendClassObject::new(self).into()) } } - impl $crate::php::types::zval::IntoZval for $type { - const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some( - <$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME, + impl $crate::convert::IntoZval for $type { + const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some( + <$type as $crate::class::RegisteredClass>::CLASS_NAME, )); #[inline] fn set_zval( self, - zv: &mut $crate::php::types::zval::Zval, + zv: &mut $crate::types::Zval, persistent: bool, - ) -> $crate::errors::Result<()> { - use $crate::php::types::object::IntoZendObject; + ) -> $crate::error::Result<()> { + use $crate::convert::IntoZendObject; self.into_zend_object()?.set_zval(zv, persistent) } } }; } + +/// Derives `From for Zval` and `IntoZval` for a given type. +macro_rules! into_zval { + ($type: ty, $fn: ident, $dt: ident) => { + impl From<$type> for $crate::types::Zval { + fn from(val: $type) -> Self { + let mut zv = Self::new(); + zv.$fn(val); + zv + } + } + + impl $crate::convert::IntoZval for $type { + const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt; + + fn set_zval(self, zv: &mut $crate::types::Zval, _: bool) -> $crate::error::Result<()> { + zv.$fn(self); + Ok(()) + } + } + }; +} + +/// Derives `TryFrom for T` and `FromZval for T` on a given type. +macro_rules! try_from_zval { + ($type: ty, $fn: ident, $dt: ident) => { + impl $crate::convert::FromZval<'_> for $type { + const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt; + + fn from_zval(zval: &$crate::types::Zval) -> ::std::option::Option { + use ::std::convert::TryInto; + + zval.$fn().and_then(|val| val.try_into().ok()) + } + } + + impl ::std::convert::TryFrom<$crate::types::Zval> for $type { + type Error = $crate::error::Error; + + fn try_from(value: $crate::types::Zval) -> $crate::error::Result { + ::from_zval(&value) + .ok_or($crate::error::Error::ZvalConversion(value.get_type())) + } + } + }; +} + +pub(crate) use into_zval; +pub(crate) use try_from_zval; diff --git a/src/php/enums.rs b/src/php/enums.rs deleted file mode 100644 index 63a89481bb..0000000000 --- a/src/php/enums.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! Wrapper for enums introduced in C. - -use std::{convert::TryFrom, fmt::Display}; - -use crate::{ - bindings::{ - IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED, IS_NULL, - IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_UNDEF, IS_VOID, - _IS_BOOL, - }, - errors::{Error, Result}, - php::flags::ZvalTypeFlags, -}; - -/// Valid data types for PHP. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum DataType { - Undef, - Null, - False, - True, - Long, - Double, - String, - Array, - Object(Option<&'static str>), - Resource, - Reference, - Callable, - ConstantExpression, - Void, - Mixed, - Bool, - Ptr, -} - -impl Default for DataType { - fn default() -> Self { - Self::Void - } -} - -impl DataType { - /// Returns the integer representation of the data type. - pub const fn as_u32(&self) -> u32 { - match self { - DataType::Undef => IS_UNDEF, - DataType::Null => IS_NULL, - DataType::False => IS_FALSE, - DataType::True => IS_TRUE, - DataType::Long => IS_LONG, - DataType::Double => IS_DOUBLE, - DataType::String => IS_STRING, - DataType::Array => IS_ARRAY, - DataType::Object(_) => IS_OBJECT, - DataType::Resource => IS_RESOURCE, - DataType::Reference => IS_RESOURCE, - DataType::Callable => IS_CALLABLE, - DataType::ConstantExpression => IS_CONSTANT_AST, - DataType::Void => IS_VOID, - DataType::Mixed => IS_MIXED, - DataType::Bool => _IS_BOOL, - DataType::Ptr => IS_PTR, - } - } -} - -// TODO: Ideally want something like this -// pub struct Type { -// data_type: DataType, -// is_refcounted: bool, -// is_collectable: bool, -// is_immutable: bool, -// is_persistent: bool, -// } -// -// impl From for Type { ... } - -impl TryFrom for DataType { - type Error = Error; - - fn try_from(value: ZvalTypeFlags) -> Result { - macro_rules! contains { - ($t: ident) => { - if value.contains(ZvalTypeFlags::$t) { - return Ok(DataType::$t); - } - }; - } - - contains!(Undef); - contains!(Null); - contains!(False); - contains!(True); - contains!(False); - contains!(Long); - contains!(Double); - contains!(String); - contains!(Array); - contains!(Resource); - contains!(Callable); - contains!(ConstantExpression); - contains!(Void); - - if value.contains(ZvalTypeFlags::Object) { - return Ok(DataType::Object(None)); - } - - Err(Error::UnknownDatatype(0)) - } -} - -impl From for DataType { - #[allow(clippy::bad_bit_mask)] - fn from(value: u32) -> Self { - macro_rules! contains { - ($c: ident, $t: ident) => { - if (value & $c) == $c { - return DataType::$t; - } - }; - } - - contains!(IS_VOID, Void); - contains!(IS_CALLABLE, Callable); - contains!(IS_CONSTANT_AST, ConstantExpression); - contains!(IS_REFERENCE, Reference); - contains!(IS_RESOURCE, Resource); - contains!(IS_ARRAY, Array); - contains!(IS_STRING, String); - contains!(IS_DOUBLE, Double); - contains!(IS_LONG, Long); - contains!(IS_TRUE, True); - contains!(IS_FALSE, False); - contains!(IS_NULL, Null); - contains!(IS_PTR, Ptr); - - if (value & IS_OBJECT) == IS_OBJECT { - return DataType::Object(None); - } - - contains!(IS_UNDEF, Undef); - - DataType::Mixed - } -} - -impl Display for DataType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DataType::Undef => write!(f, "Undefined"), - DataType::Null => write!(f, "Null"), - DataType::False => write!(f, "False"), - DataType::True => write!(f, "True"), - DataType::Long => write!(f, "Long"), - DataType::Double => write!(f, "Double"), - DataType::String => write!(f, "String"), - DataType::Array => write!(f, "Array"), - DataType::Object(obj) => write!(f, "{}", obj.as_deref().unwrap_or("Object")), - DataType::Resource => write!(f, "Resource"), - DataType::Reference => write!(f, "Reference"), - DataType::Callable => write!(f, "Callable"), - DataType::ConstantExpression => write!(f, "Constant Expression"), - DataType::Void => write!(f, "Void"), - DataType::Bool => write!(f, "Bool"), - DataType::Mixed => write!(f, "Mixed"), - DataType::Ptr => write!(f, "Pointer"), - } - } -} - -#[cfg(test)] -mod tests { - use super::DataType; - use crate::bindings::{ - IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, - IS_FALSE, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_REFERENCE, - IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, IS_TRUE, IS_UNDEF, - IS_VOID, - }; - use std::convert::TryFrom; - - #[test] - fn test_datatype() { - macro_rules! test { - ($c: ident, $t: ident) => { - assert_eq!(DataType::try_from($c), Ok(DataType::$t)); - }; - } - - test!(IS_UNDEF, Undef); - test!(IS_NULL, Null); - test!(IS_FALSE, False); - test!(IS_TRUE, True); - test!(IS_LONG, Long); - test!(IS_DOUBLE, Double); - test!(IS_STRING, String); - test!(IS_ARRAY, Array); - assert_eq!(DataType::try_from(IS_OBJECT), Ok(DataType::Object(None))); - test!(IS_RESOURCE, Resource); - test!(IS_REFERENCE, Reference); - test!(IS_CONSTANT_AST, ConstantExpression); - test!(IS_CALLABLE, Callable); - test!(IS_VOID, Void); - - test!(IS_INTERNED_STRING_EX, String); - test!(IS_STRING_EX, String); - test!(IS_ARRAY_EX, Array); - assert_eq!(DataType::try_from(IS_OBJECT_EX), Ok(DataType::Object(None))); - test!(IS_RESOURCE_EX, Resource); - test!(IS_REFERENCE_EX, Reference); - test!(IS_CONSTANT_AST_EX, ConstantExpression); - } -} diff --git a/src/php/flags.rs b/src/php/flags.rs deleted file mode 100644 index 8f52b50063..0000000000 --- a/src/php/flags.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Bitflags used in PHP and the Zend engine. - -use bitflags::bitflags; - -use crate::bindings::{ - CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, IS_ARRAY, IS_CALLABLE, - IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, - IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, - ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, - ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, - ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, - ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, - ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE, - ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, - ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, - ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED, - ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT, - ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, - ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, - ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS, - Z_TYPE_FLAGS_SHIFT, -}; - -bitflags! { - /// Flags used for setting the type of Zval. - pub struct ZvalTypeFlags: u32 { - const Undef = IS_UNDEF; - const Null = IS_NULL; - const False = IS_FALSE; - const True = IS_TRUE; - const Long = IS_LONG; - const Double = IS_DOUBLE; - const String = IS_STRING; - const Array = IS_ARRAY; - const Object = IS_OBJECT; - const Resource = IS_RESOURCE; - const Reference = IS_REFERENCE; - const Callable = IS_CALLABLE; - const ConstantExpression = IS_CONSTANT_AST; - const Void = IS_VOID; - const Ptr = IS_PTR; - - const InternedStringEx = Self::String.bits; - const StringEx = Self::String.bits | Self::RefCounted.bits; - const ArrayEx = Self::Array.bits | Self::RefCounted.bits | Self::Collectable.bits; - const ObjectEx = Self::Object.bits | Self::RefCounted.bits | Self::Collectable.bits; - const ResourceEx = Self::Resource.bits | Self::RefCounted.bits; - const ReferenceEx = Self::Reference.bits | Self::RefCounted.bits; - const ConstantAstEx = Self::ConstantExpression.bits | Self::RefCounted.bits; - - const RefCounted = (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT); - const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT); - } -} - -bitflags! { - /// Flags for building classes. - pub struct ClassFlags: u32 { - const Final = ZEND_ACC_FINAL; - const Abstract = ZEND_ACC_ABSTRACT; - const Immutable = ZEND_ACC_IMMUTABLE; - const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS; - const TopLevel = ZEND_ACC_TOP_LEVEL; - const Preloaded = ZEND_ACC_PRELOADED; - - const Interface = ZEND_ACC_INTERFACE; - const Trait = ZEND_ACC_TRAIT; - const AnonymousClass = ZEND_ACC_ANON_CLASS; - const Linked = ZEND_ACC_LINKED; - const ImplicitAbstractClass = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; - const UseGuards = ZEND_ACC_USE_GUARDS; - const ConstantsUpdated = ZEND_ACC_CONSTANTS_UPDATED; - const NoDynamicProperties = ZEND_ACC_NO_DYNAMIC_PROPERTIES; - const HasStaticInMethods = ZEND_HAS_STATIC_IN_METHODS; - const PropertyTypesResolved = ZEND_ACC_PROPERTY_TYPES_RESOLVED; - const ReuseGetIterator = ZEND_ACC_REUSE_GET_ITERATOR; - const ResolvedParent = ZEND_ACC_RESOLVED_PARENT; - const ResolvedInterfaces = ZEND_ACC_RESOLVED_INTERFACES; - const UnresolvedVariance = ZEND_ACC_UNRESOLVED_VARIANCE; - const NearlyLinked = ZEND_ACC_NEARLY_LINKED; - const HasUnlinkedUses = ZEND_ACC_HAS_UNLINKED_USES; - } -} - -bitflags! { - /// Flags for building methods. - pub struct MethodFlags: u32 { - const Public = ZEND_ACC_PUBLIC; - const Protected = ZEND_ACC_PROTECTED; - const Private = ZEND_ACC_PRIVATE; - const Changed = ZEND_ACC_CHANGED; - const Static = ZEND_ACC_STATIC; - const Final = ZEND_ACC_FINAL; - const Abstract = ZEND_ACC_ABSTRACT; - const Immutable = ZEND_ACC_IMMUTABLE; - const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS; - const TopLevel = ZEND_ACC_TOP_LEVEL; - const Preloaded = ZEND_ACC_PRELOADED; - - const Deprecated = ZEND_ACC_DEPRECATED; - const ReturnReference = ZEND_ACC_RETURN_REFERENCE; - const HasReturnType = ZEND_ACC_HAS_RETURN_TYPE; - const Variadic = ZEND_ACC_VARIADIC; - const HasFinallyBlock = ZEND_ACC_HAS_FINALLY_BLOCK; - const EarlyBinding = ZEND_ACC_EARLY_BINDING; - const UsesThis = ZEND_ACC_USES_THIS; - const CallViaTrampoline = ZEND_ACC_CALL_VIA_TRAMPOLINE; - const NeverCache = ZEND_ACC_NEVER_CACHE; - const TraitClone = ZEND_ACC_TRAIT_CLONE; - const IsConstructor = ZEND_ACC_CTOR; - const Closure = ZEND_ACC_CLOSURE; - const FakeClosure = ZEND_ACC_FAKE_CLOSURE; - const Generator = ZEND_ACC_GENERATOR; - const DonePassTwo = ZEND_ACC_DONE_PASS_TWO; - const HeapRTCache = ZEND_ACC_HEAP_RT_CACHE; - const StrictTypes = ZEND_ACC_STRICT_TYPES; - } -} - -bitflags! { - /// Flags for building properties. - pub struct PropertyFlags: u32 { - const Public = ZEND_ACC_PUBLIC; - const Protected = ZEND_ACC_PROTECTED; - const Private = ZEND_ACC_PRIVATE; - const Changed = ZEND_ACC_CHANGED; - const Static = ZEND_ACC_STATIC; - const Promoted = ZEND_ACC_PROMOTED; - } -} - -bitflags! { - /// Flags for building constants. - pub struct ConstantFlags: u32 { - const Public = ZEND_ACC_PUBLIC; - const Protected = ZEND_ACC_PROTECTED; - const Private = ZEND_ACC_PRIVATE; - const Promoted = ZEND_ACC_PROMOTED; - } -} - -bitflags! { - /// Flags for building module global constants. - pub struct GlobalConstantFlags: u32 { - const CaseSensitive = CONST_CS; - const Persistent = CONST_PERSISTENT; - const NoFileCache = CONST_NO_FILE_CACHE; - const Deprecated = CONST_DEPRECATED; - } -} - -bitflags! { - /// Represents the result of a function. - pub struct ZendResult: i32 { - const Success = 0; - const Failure = -1; - } -} diff --git a/src/php/mod.rs b/src/php/mod.rs deleted file mode 100644 index 0c8f8b7f75..0000000000 --- a/src/php/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Objects relating to PHP and the Zend engine. - -pub mod alloc; -pub mod args; -pub mod boxed; -pub mod class; -pub mod constants; -pub mod enums; -pub mod exceptions; -pub mod execution_data; -pub mod flags; -pub mod function; -pub mod globals; -pub mod module; -pub mod pack; -pub mod types; diff --git a/src/php/types/binary.rs b/src/php/types/binary.rs deleted file mode 100644 index 9bdfe1d4ca..0000000000 --- a/src/php/types/binary.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Types relating to binary data transmission between Rust and PHP. - -use std::{ - convert::TryFrom, - iter::FromIterator, - ops::{Deref, DerefMut}, -}; - -use crate::{ - errors::{Error, Result}, - php::{enums::DataType, pack::Pack}, -}; - -use super::zval::{FromZval, IntoZval, Zval}; - -/// Acts as a wrapper around [`Vec`] where `T` implements [`Pack`]. Primarily used for passing -/// binary data into Rust functions. Can be treated as a [`Vec`] in most situations, or can be -/// 'unwrapped' into a [`Vec`] through the [`From`] implementation on [`Vec`]. -#[derive(Debug)] -pub struct Binary(Vec); - -impl Binary { - /// Creates a new binary wrapper from a set of data which can be converted into a vector. - /// - /// # Parameters - /// - /// * `data` - Data to store inside the binary wrapper. - pub fn new(data: impl Into>) -> Self { - Self(data.into()) - } -} - -impl Deref for Binary { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Binary { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl FromZval<'_> for Binary { - const TYPE: DataType = DataType::String; - - fn from_zval(zval: &Zval) -> Option { - zval.binary().map(Binary) - } -} - -impl TryFrom for Binary { - type Error = Error; - - fn try_from(value: Zval) -> Result { - Self::from_zval(&value).ok_or_else(|| Error::ZvalConversion(value.get_type())) - } -} - -impl IntoZval for Binary { - const TYPE: DataType = DataType::String; - - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - zv.set_binary(self.0); - Ok(()) - } -} - -impl From> for Vec { - fn from(value: Binary) -> Self { - value.0 - } -} - -impl From> for Binary { - fn from(value: Vec) -> Self { - Self::new(value) - } -} - -impl FromIterator for Binary { - fn from_iter>(iter: U) -> Self { - Self(iter.into_iter().collect::>()) - } -} diff --git a/src/php/types/long.rs b/src/php/types/long.rs deleted file mode 100644 index 1ba3d7af21..0000000000 --- a/src/php/types/long.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Represents an integer introduced in PHP. Note that the size of this integer differs. -//! On a 32-bit system, a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. - -use crate::bindings::zend_long; - -/// Internal identifier used for a long. -/// The size depends on the system architecture. On 32-bit systems, -/// a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. -pub type ZendLong = zend_long; diff --git a/src/php/types/object.rs b/src/php/types/object.rs deleted file mode 100644 index dc15820f5e..0000000000 --- a/src/php/types/object.rs +++ /dev/null @@ -1,1074 +0,0 @@ -//! Represents an object in PHP. Allows for overriding the internal object used by classes, -//! allowing users to store Rust data inside a PHP object. - -use std::{ - collections::HashMap, - convert::TryInto, - ffi::c_void, - fmt::Debug, - marker::PhantomData, - mem::{self, MaybeUninit}, - ops::{Deref, DerefMut}, - os::raw::c_int, - ptr::{self, NonNull}, - sync::atomic::{AtomicBool, AtomicPtr, Ordering}, -}; - -use crate::{ - bindings::{ - ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init, - std_object_handlers, zend_call_known_function, zend_is_true, zend_object, - zend_object_handlers, zend_object_std_dtor, zend_object_std_init, - zend_objects_clone_members, zend_objects_new, zend_standard_class_def, - zend_std_get_properties, zend_std_has_property, zend_std_read_property, - zend_std_write_property, zend_string, HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, - ZEND_PROPERTY_ISSET, - }, - errors::{Error, Result}, - php::{ - boxed::{ZBox, ZBoxable}, - class::ClassEntry, - enums::DataType, - exceptions::{PhpException, PhpResult}, - execution_data::ExecutionData, - flags::ZvalTypeFlags, - function::FunctionBuilder, - globals::ExecutorGlobals, - }, -}; - -use super::{ - props::Property, - rc::PhpRc, - string::ZendStr, - zval::{FromZval, FromZvalMut, IntoZval, Zval}, -}; - -pub type ZendObject = zend_object; -pub type ZendObjectHandlers = zend_object_handlers; - -/// Different ways to query if a property exists. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(u32)] -pub enum PropertyQuery { - /// Property exists and is not NULL. - Isset = ZEND_PROPERTY_ISSET, - /// Property is not empty. - NotEmpty = ZEND_ISEMPTY, - /// Property exists. - Exists = ZEND_PROPERTY_EXISTS, -} - -impl ZendObject { - /// Creates a new [`ZendObject`], returned inside an [`ZBox`] wrapper. - /// - /// # Parameters - /// - /// * `ce` - The type of class the new object should be an instance of. - /// - /// # Panics - /// - /// Panics when allocating memory for the new object fails. - pub fn new(ce: &ClassEntry) -> ZBox { - // SAFETY: Using emalloc to allocate memory inside Zend arena. Casting `ce` to `*mut` is valid - // as the function will not mutate `ce`. - unsafe { - let ptr = zend_objects_new(ce as *const _ as *mut _); - ZBox::from_raw( - ptr.as_mut() - .expect("Failed to allocate memory for Zend object"), - ) - } - } - - /// Creates a new `stdClass` instance, returned inside an [`ZBox`] wrapper. - /// - /// # Panics - /// - /// Panics if allocating memory for the object fails, or if the `stdClass` class entry has not been - /// registered with PHP yet. - pub fn new_stdclass() -> ZBox { - // SAFETY: This will be `NULL` until it is initialized. `as_ref()` checks for null, - // so we can panic if it's null. - Self::new(unsafe { - zend_standard_class_def - .as_ref() - .expect("`stdClass` class instance not initialized yet") - }) - } - - /// Converts the class object into an owned [`ZendObject`]. This removes any possibility of - /// accessing the underlying attached Rust struct. - pub fn from_class_object(obj: ZBox>) -> ZBox { - let this = obj.into_raw(); - // SAFETY: Consumed box must produce a well-aligned non-null pointer. - unsafe { ZBox::from_raw(&mut this.std) } - } - - /// Attempts to retrieve the class name of the object. - pub fn get_class_name(&self) -> Result { - unsafe { - self.handlers()? - .get_class_name - .and_then(|f| f(self).as_ref()) - .ok_or(Error::InvalidScope) - .and_then(|s| s.try_into()) - } - } - - /// Checks if the given object is an instance of a registered class with Rust - /// type `T`. - pub fn is_instance(&self) -> bool { - (self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _)) - } - - /// Attempts to read a property from the Object. Returns a result containing the - /// value of the property if it exists and can be read, and an [`Error`] otherwise. - /// - /// # Parameters - /// - /// * `name` - The name of the property. - /// * `query` - The type of query to use when attempting to get a property. - pub fn get_property<'a, T>(&'a self, name: &str) -> Result - where - T: FromZval<'a>, - { - if !self.has_property(name, PropertyQuery::Exists)? { - return Err(Error::InvalidProperty); - } - - let mut name = ZendStr::new(name, false)?; - let mut rv = Zval::new(); - - let zv = unsafe { - self.handlers()?.read_property.ok_or(Error::InvalidScope)?( - self.mut_ptr(), - name.deref_mut(), - 1, - std::ptr::null_mut(), - &mut rv, - ) - .as_ref() - } - .ok_or(Error::InvalidScope)?; - - T::from_zval(zv).ok_or_else(|| Error::ZvalConversion(zv.get_type())) - } - - /// Attempts to set a property on the object. - /// - /// # Parameters - /// - /// * `name` - The name of the property. - /// * `value` - The value to set the property to. - pub fn set_property(&mut self, name: &str, value: impl IntoZval) -> Result<()> { - let mut name = ZendStr::new(name, false)?; - let mut value = value.into_zval(false)?; - - unsafe { - self.handlers()?.write_property.ok_or(Error::InvalidScope)?( - self, - name.deref_mut(), - &mut value, - std::ptr::null_mut(), - ) - .as_ref() - } - .ok_or(Error::InvalidScope)?; - Ok(()) - } - - /// Checks if a property exists on an object. Takes a property name and query parameter, - /// which defines what classifies if a property exists or not. See [`PropertyQuery`] for - /// more information. - /// - /// # Parameters - /// - /// * `name` - The name of the property. - /// * `query` - The 'query' to classify if a property exists. - pub fn has_property(&self, name: &str, query: PropertyQuery) -> Result { - let mut name = ZendStr::new(name, false)?; - - Ok(unsafe { - self.handlers()?.has_property.ok_or(Error::InvalidScope)?( - self.mut_ptr(), - name.deref_mut(), - query as _, - std::ptr::null_mut(), - ) - } > 0) - } - - /// Attempts to retrieve the properties of the object. Returned inside a Zend Hashtable. - pub fn get_properties(&self) -> Result<&HashTable> { - unsafe { - self.handlers()? - .get_properties - .and_then(|props| props(self.mut_ptr()).as_ref()) - .ok_or(Error::InvalidScope) - } - } - - /// Attempts to retrieve a reference to the object handlers. - #[inline] - unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> { - self.handlers.as_ref().ok_or(Error::InvalidScope) - } - - /// Returns a mutable pointer to `self`, regardless of the type of reference. - /// Only to be used in situations where a C function requires a mutable pointer - /// but does not modify the underlying data. - #[inline] - fn mut_ptr(&self) -> *mut Self { - (self as *const Self) as *mut Self - } - - /// Extracts some type from a Zend object. - /// - /// This is a wrapper function around `FromZendObject::extract()`. - pub fn extract<'a, T>(&'a self) -> Result - where - T: FromZendObject<'a>, - { - T::from_zend_object(self) - } -} - -unsafe impl ZBoxable for ZendObject { - fn free(&mut self) { - unsafe { ext_php_rs_zend_object_release(self) } - } -} - -impl Debug for ZendObject { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut dbg = f.debug_struct( - self.get_class_name() - .unwrap_or_else(|_| "ZendObject".to_string()) - .as_str(), - ); - - if let Ok(props) = self.get_properties() { - for (id, key, val) in props.iter() { - dbg.field(key.unwrap_or_else(|| id.to_string()).as_str(), val); - } - } - - dbg.finish() - } -} - -impl<'a> FromZval<'a> for &'a ZendObject { - const TYPE: DataType = DataType::Object(None); - - fn from_zval(zval: &'a Zval) -> Option { - zval.object() - } -} - -impl IntoZval for ZBox { - const TYPE: DataType = DataType::Object(None); - - fn set_zval(mut self, zv: &mut Zval, _: bool) -> Result<()> { - // We must decrement the refcounter on the object before inserting into the zval, - // as the reference counter will be incremented on add. - // NOTE(david): again is this needed, we increment in `set_object`. - self.dec_count(); - zv.set_object(self.into_raw()); - Ok(()) - } -} - -/// `FromZendObject` is implemented by types which can be extracted from a Zend object. -/// -/// Normal usage is through the helper method `ZendObject::extract`: -/// -/// ```rust,ignore -/// let obj: ZendObject = ...; -/// let repr: String = obj.extract(); -/// let props: HashMap = obj.extract(); -/// ``` -/// -/// Should be functionally equivalent to casting an object to another compatable type. -pub trait FromZendObject<'a>: Sized { - /// Extracts `Self` from the source `ZendObject`. - fn from_zend_object(obj: &'a ZendObject) -> Result; -} - -/// Implemented on types which can be extracted from a mutable zend object. -/// -/// If `Self` does not require the object to be mutable, it should implement -/// [`FromZendObject`] instead, as this trait is generically implemented for -/// any types that also implement [`FromZendObject`]. -pub trait FromZendObjectMut<'a>: Sized { - /// Extracts `Self` from the source `ZendObject`. - fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result; -} - -/// Implemented on types which can be converted into a Zend object. It is up to the implementation -/// to determine the type of object which is produced. -pub trait IntoZendObject { - /// Attempts to convert `self` into a Zend object. - fn into_zend_object(self) -> Result>; -} - -impl FromZendObject<'_> for String { - fn from_zend_object(obj: &ZendObject) -> Result { - let mut ret = Zval::new(); - unsafe { - zend_call_known_function( - (*obj.ce).__tostring, - obj as *const _ as *mut _, - obj.ce, - &mut ret, - 0, - std::ptr::null_mut(), - std::ptr::null_mut(), - ); - } - - if let Some(err) = ExecutorGlobals::take_exception() { - // TODO: become an error - let class_name = obj.get_class_name(); - panic!( - "Uncaught exception during call to {}::__toString(): {:?}", - class_name.expect("unable to determine class name"), - err - ); - } else if let Some(output) = ret.extract() { - Ok(output) - } else { - // TODO: become an error - let class_name = obj.get_class_name(); - panic!( - "{}::__toString() must return a string", - class_name.expect("unable to determine class name"), - ); - } - } -} - -/// Wrapper struct used to return a reference to a PHP object. -pub struct ClassRef<'a, T: RegisteredClass + 'a> { - ptr: &'a mut ZendClassObject, -} - -impl<'a, T: RegisteredClass> ClassRef<'a, T> { - /// Creates a new class reference from a Rust type reference. - pub fn from_ref(obj: &'a T) -> Option { - let ptr = unsafe { ZendClassObject::from_obj_ptr(obj)? }; - Some(Self { ptr }) - } -} - -impl<'a, T: RegisteredClass> IntoZval for ClassRef<'a, T> { - const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); - - #[inline] - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - zv.set_object(&mut self.ptr.std); - Ok(()) - } -} - -impl From>> for ZBox { - #[inline] - fn from(obj: ZBox>) -> Self { - ZendObject::from_class_object(obj) - } -} - -impl Default for ZBox> { - #[inline] - fn default() -> Self { - ZendClassObject::new(T::default()) - } -} - -impl Clone for ZBox> { - fn clone(&self) -> Self { - // SAFETY: All constructors of `NewClassObject` guarantee that it will contain a valid pointer. - // The constructor also guarantees that the internal `ZendClassObject` pointer will contain a valid, - // initialized `obj`, therefore we can dereference both safely. - unsafe { - let mut new = ZendClassObject::new((&***self).clone()); - zend_objects_clone_members(&mut new.std, &self.std as *const _ as *mut _); - new - } - } -} - -impl Debug for ZendClassObject { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - (&**self).fmt(f) - } -} - -impl IntoZval for ZBox> { - const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); - - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - let obj = self.into_raw(); - zv.set_object(&mut obj.std); - Ok(()) - } -} - -/// Object constructor metadata. -pub struct ConstructorMeta { - /// Constructor function. - pub constructor: fn(&mut ExecutionData) -> ConstructorResult, - /// Function called to build the constructor function. Usually adds arguments. - pub build_fn: fn(FunctionBuilder) -> FunctionBuilder, -} - -/// Implemented on Rust types which are exported to PHP. Allows users to get and set PHP properties on -/// the object. -pub trait RegisteredClass: Sized -where - Self: 'static, -{ - /// PHP class name of the registered class. - const CLASS_NAME: &'static str; - - /// Optional class constructor. - const CONSTRUCTOR: Option> = None; - - /// Returns a reference to the class metadata, which stores the class entry and handlers. - /// - /// This must be statically allocated, and is usually done through the [`macro@php_class`] - /// macro. - /// - /// [`macro@php_class`]: crate::php_class - fn get_metadata() -> &'static ClassMetadata; - - /// Attempts to retrieve a property from the class object. - /// - /// # Parameters - /// - /// * `name` - The name of the property. - /// - /// # Returns - /// - /// Returns a given type `T` inside an option which is the value of the zval, or [`None`] - /// if the property could not be found. - /// - /// # Safety - /// - /// Caller must guarantee that the object the function is called on is immediately followed - /// by a [`zend_object`], which is true when the object was instantiated by PHP. - unsafe fn get_property<'a, T: FromZval<'a>>(&'a self, name: &str) -> Option { - let obj = ZendClassObject::::from_obj_ptr(self)?; - obj.std.get_property(name).ok() - } - - /// Attempts to set the value of a property on the class object. - /// - /// # Parameters - /// - /// * `name` - The name of the property to set. - /// * `value` - The value to set the property to. - /// - /// # Returns - /// - /// Returns nothing in an option if the property was successfully set. Returns none if setting - /// the value failed. - /// - /// # Safety - /// - /// Caller must guarantee that the object the function is called on is immediately followed - /// by a [`zend_object`], which is true when the object was instantiated by PHP. - unsafe fn set_property(&mut self, name: &str, value: impl IntoZval) -> Option<()> { - let obj = ZendClassObject::::from_obj_ptr(self)?; - obj.std.set_property(name, value).ok()?; - Some(()) - } - - /// Returns a hash table containing the properties of the class. - /// - /// The key should be the name of the property and the value should be a reference to the property - /// with reference to `self`. The value is a [`Property`]. - fn get_properties<'a>() -> HashMap<&'static str, Property<'a, Self>>; -} - -/// Representation of a Zend class object in memory. -#[repr(C)] -pub struct ZendClassObject { - obj: Option, - std: zend_object, -} - -impl ZendClassObject { - /// Creates a new [`ZendClassObject`] of type `T`, where `T` is a [`RegisteredClass`] in PHP, storing the - /// given value `val` inside the object. - /// - /// # Parameters - /// - /// * `val` - The value to store inside the object. - /// - /// # Panics - /// - /// Panics if memory was unable to be allocated for the new object. - pub fn new(val: T) -> ZBox { - unsafe { Self::internal_new(Some(val)) } - } - - /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized internal object. - /// - /// # Safety - /// - /// As the object is uninitialized, the caller must ensure the following until the internal object is - /// initialized: - /// - /// * The object is never dereferenced to `T`. - /// * The [`Clone`] implementation is never called. - /// * The [`Debug`] implementation is never called. - /// - /// If any of these conditions are not met while not initialized, the corresponding function will panic. - /// Converting the object into its inner pointer with the [`into_raw`] function is valid, however. - /// - /// [`into_raw`]: #method.into_raw - /// - /// # Panics - /// - /// Panics if memory was unable to be allocated for the new object. - pub unsafe fn new_uninit() -> ZBox { - Self::internal_new(None) - } - - /// Creates a new [`ZendObject`] of type `T`, storing the given (and potentially uninitialized) `val` - /// inside the object. - /// - /// # Parameters - /// - /// * `val` - Value to store inside the object. See safety section. - /// * `init` - Whether the given `val` was initialized. - /// - /// # Safety - /// - /// Providing an initialized variant of [`MaybeUninit`] is safe. - /// - /// Providing an uninitalized variant of [`MaybeUninit`] is unsafe. As the object is uninitialized, - /// the caller must ensure the following until the internal object is initialized: - /// - /// * The object is never dereferenced to `T`. - /// * The [`Clone`] implementation is never called. - /// * The [`Debug`] implementation is never called. - /// - /// If any of these conditions are not met while not initialized, the corresponding function will panic. - /// Converting the object into its inner with the [`into_raw`] function is valid, however. You can initialize - /// the object with the [`initialize`] function. - /// - /// [`into_raw`]: #method.into_raw - /// [`initialize`]: #method.initialize - /// - /// # Panics - /// - /// Panics if memory was unable to be allocated for the new object. - unsafe fn internal_new(val: Option) -> ZBox { - let size = mem::size_of::>(); - let meta = T::get_metadata(); - let ce = meta.ce() as *const _ as *mut _; - let obj = ext_php_rs_zend_object_alloc(size as _, ce) as *mut ZendClassObject; - let obj = obj - .as_mut() - .expect("Failed to allocate for new Zend object"); - - zend_object_std_init(&mut obj.std, ce); - object_properties_init(&mut obj.std, ce); - - // SAFETY: `obj` is non-null and well aligned as it is a reference. - // As the data in `obj.obj` is uninitalized, we don't want to drop - // the data, but directly override it. - ptr::write(&mut obj.obj, val); - - obj.std.handlers = meta.handlers(); - ZBox::from_raw(obj) - } - - /// Initializes the class object with the value `val`. - /// - /// # Parameters - /// - /// * `val` - The value to initialize the object with. - /// - /// # Returns - /// - /// Returns the old value in an [`Option`] if the object had already been initialized, [`None`] - /// otherwise. - pub fn initialize(&mut self, val: T) -> Option { - self.obj.replace(val) - } - - /// Returns a reference to the [`ZendClassObject`] of a given object `T`. Returns [`None`] - /// if the given object is not of the type `T`. - /// - /// # Parameters - /// - /// * `obj` - The object to get the [`ZendClassObject`] for. - /// - /// # Safety - /// - /// Caller must guarantee that the given `obj` was created by Zend, which means that it - /// is immediately followed by a [`zend_object`]. - pub(crate) unsafe fn from_obj_ptr(obj: &T) -> Option<&mut Self> { - // TODO(david): Remove this function - let ptr = (obj as *const T as *mut Self).as_mut()?; - - if ptr.std.is_instance::() { - Some(ptr) - } else { - None - } - } - - /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. - /// Returns [`None`] if the given object is not of the type `T`. - /// - /// # Parameters - /// - /// * `obj` - The zend object to get the [`ZendClassObject`] for. - pub fn from_zend_obj(std: &zend_object) -> Option<&Self> { - Some(Self::_from_zend_obj(std)?) - } - - /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. - /// Returns [`None`] if the given object is not of the type `T`. - /// - /// # Parameters - /// - /// * `obj` - The zend object to get the [`ZendClassObject`] for. - pub fn from_zend_obj_mut(std: &mut zend_object) -> Option<&mut Self> { - Self::_from_zend_obj(std) - } - - fn _from_zend_obj(std: &zend_object) -> Option<&mut Self> { - let std = std as *const zend_object as *const i8; - let ptr = unsafe { - let ptr = std.offset(0 - Self::std_offset() as isize) as *const Self; - (ptr as *mut Self).as_mut()? - }; - - if ptr.std.is_instance::() { - Some(ptr) - } else { - None - } - } - - /// Returns a mutable reference to the underlying Zend object. - pub fn get_mut_zend_obj(&mut self) -> &mut zend_object { - &mut self.std - } - - /// Returns the offset of the `std` property in the class object. - pub(crate) fn std_offset() -> usize { - unsafe { - let null = NonNull::::dangling(); - let base = null.as_ref() as *const Self; - let std = &null.as_ref().std as *const zend_object; - - (std as usize) - (base as usize) - } - } -} - -impl<'a, T: RegisteredClass> FromZval<'a> for &'a ZendClassObject { - const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); - - fn from_zval(zval: &'a Zval) -> Option { - Self::from_zend_object(zval.object()?).ok() - } -} - -impl<'a, T: RegisteredClass> FromZendObject<'a> for &'a ZendClassObject { - fn from_zend_object(obj: &'a ZendObject) -> Result { - // TODO(david): replace with better error - ZendClassObject::from_zend_obj(obj).ok_or(Error::InvalidScope) - } -} - -impl<'a, T: RegisteredClass> FromZvalMut<'a> for &'a mut ZendClassObject { - const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); - - fn from_zval_mut(zval: &'a mut Zval) -> Option { - Self::from_zend_object_mut(zval.object_mut()?).ok() - } -} - -impl<'a, T: RegisteredClass> FromZendObjectMut<'a> for &'a mut ZendClassObject { - fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result { - ZendClassObject::from_zend_obj_mut(obj).ok_or(Error::InvalidScope) - } -} - -unsafe impl ZBoxable for ZendClassObject { - fn free(&mut self) { - // SAFETY: All constructors guarantee that `self` contains a valid pointer. Further, all constructors - // guarantee that the `std` field of `ZendClassObject` will be initialized. - unsafe { ext_php_rs_zend_object_release(&mut self.std) } - } -} - -impl Deref for ZendClassObject { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.obj - .as_ref() - .expect("Attempted to access uninitalized class object") - } -} - -impl DerefMut for ZendClassObject { - fn deref_mut(&mut self) -> &mut Self::Target { - self.obj - .as_mut() - .expect("Attempted to access uninitalized class object") - } -} - -/// Stores the class entry and handlers for a Rust type which has been exported to PHP. -pub struct ClassMetadata { - handlers_init: AtomicBool, - handlers: MaybeUninit, - ce: AtomicPtr, - - phantom: PhantomData, -} - -impl ClassMetadata { - /// Creates a new class metadata instance. - pub const fn new() -> Self { - Self { - handlers_init: AtomicBool::new(false), - handlers: MaybeUninit::uninit(), - ce: AtomicPtr::new(std::ptr::null_mut()), - phantom: PhantomData, - } - } -} - -impl ClassMetadata { - /// Returns an immutable reference to the object handlers contained inside the class metadata. - pub fn handlers(&self) -> &ZendObjectHandlers { - self.check_handlers(); - - // SAFETY: `check_handlers` guarantees that `handlers` has been initialized. - unsafe { &*self.handlers.as_ptr() } - } - - /// Checks if the class entry has been stored, returning a boolean. - pub fn has_ce(&self) -> bool { - !self.ce.load(Ordering::SeqCst).is_null() - } - - /// Retrieves a reference to the stored class entry. - /// - /// # Panics - /// - /// Panics if there is no class entry stored inside the class metadata. - pub fn ce(&self) -> &'static ClassEntry { - // SAFETY: There are only two values that can be stored in the atomic ptr: null or a static reference - // to a class entry. On the latter case, `as_ref()` will return `None` and the function will panic. - unsafe { self.ce.load(Ordering::SeqCst).as_ref() } - .expect("Attempted to retrieve class entry before it has been stored.") - } - - /// Stores a reference to a class entry inside the class metadata. - /// - /// # Parameters - /// - /// * `ce` - The class entry to store. - /// - /// # Panics - /// - /// Panics if the class entry has already been set in the class metadata. This function should - /// only be called once. - pub fn set_ce(&self, ce: &'static mut ClassEntry) { - if !self.ce.load(Ordering::SeqCst).is_null() { - panic!("Class entry has already been set."); - } - - self.ce.store(ce, Ordering::SeqCst); - } - - /// Checks if the handlers have been initialized, and initializes them if they are not. - fn check_handlers(&self) { - if !self.handlers_init.load(Ordering::Acquire) { - // SAFETY: `MaybeUninit` has the same size as the handlers. - unsafe { ZendObjectHandlers::init::(self.handlers.as_ptr() as *mut _) }; - self.handlers_init.store(true, Ordering::Release); - } - } -} - -/// Result returned from a constructor of a class. -pub enum ConstructorResult { - /// Successfully constructed the class, contains the new class object. - Ok(T), - /// An exception occured while constructing the class. - Exception(PhpException), - /// Invalid arguments were given to the constructor. - ArgError, -} - -impl From> for ConstructorResult -where - E: Into, -{ - fn from(result: std::result::Result) -> Self { - match result { - Ok(x) => Self::Ok(x), - Err(e) => Self::Exception(e.into()), - } - } -} - -impl From for ConstructorResult { - fn from(result: T) -> Self { - Self::Ok(result) - } -} - -impl ZendObjectHandlers { - /// Initializes a given set of object handlers by copying the standard object handlers into - /// the memory location, as well as setting up the `T` type destructor. - /// - /// # Parameters - /// - //// * `ptr` - Pointer to memory location to copy the standard handlers to. - /// - /// # Safety - /// - /// Caller must guarantee that the `ptr` given is a valid memory location. - pub unsafe fn init(ptr: *mut ZendObjectHandlers) { - std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1); - let offset = ZendClassObject::::std_offset(); - (*ptr).offset = offset as _; - (*ptr).free_obj = Some(Self::free_obj::); - (*ptr).read_property = Some(Self::read_property::); - (*ptr).write_property = Some(Self::write_property::); - (*ptr).get_properties = Some(Self::get_properties::); - (*ptr).has_property = Some(Self::has_property::); - } - - unsafe extern "C" fn free_obj(object: *mut zend_object) { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .expect("Invalid object pointer given for `free_obj`"); - - // Manually drop the object as we don't want to free the underlying memory. - ptr::drop_in_place(&mut obj.obj); - - zend_object_std_dtor(object) - } - - unsafe extern "C" fn read_property( - object: *mut zend_object, - member: *mut zend_string, - type_: c_int, - cache_slot: *mut *mut c_void, - rv: *mut Zval, - ) -> *mut Zval { - #[inline(always)] - unsafe fn internal( - object: *mut zend_object, - member: *mut zend_string, - type_: c_int, - cache_slot: *mut *mut c_void, - rv: *mut Zval, - ) -> PhpResult<*mut Zval> { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; - let self_ = &mut **obj; - let mut props = T::get_properties(); - let prop = props.remove(prop_name.as_str().ok_or("Invalid property name given")?); - - // retval needs to be treated as initialized, so we set the type to null - let rv_mut = rv.as_mut().ok_or("Invalid return zval given")?; - rv_mut.u1.type_info = ZvalTypeFlags::Null.bits(); - - Ok(match prop { - Some(prop) => { - prop.get(self_, rv_mut)?; - rv - } - None => zend_std_read_property(object, member, type_, cache_slot, rv), - }) - } - - match internal::(object, member, type_, cache_slot, rv) { - Ok(rv) => rv, - Err(e) => { - let _ = e.throw(); - (&mut *rv).set_null(); - rv - } - } - } - - unsafe extern "C" fn write_property( - object: *mut zend_object, - member: *mut zend_string, - value: *mut Zval, - cache_slot: *mut *mut c_void, - ) -> *mut Zval { - #[inline(always)] - unsafe fn internal( - object: *mut zend_object, - member: *mut zend_string, - value: *mut Zval, - cache_slot: *mut *mut c_void, - ) -> PhpResult<*mut Zval> { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; - let self_ = &mut **obj; - let mut props = T::get_properties(); - let prop = props.remove(prop_name.as_str().ok_or("Invalid property name given")?); - let value_mut = value.as_mut().ok_or("Invalid return zval given")?; - - Ok(match prop { - Some(prop) => { - prop.set(self_, value_mut)?; - value - } - None => zend_std_write_property(object, member, value, cache_slot), - }) - } - - match internal::(object, member, value, cache_slot) { - Ok(rv) => rv, - Err(e) => { - let _ = e.throw(); - value - } - } - } - - unsafe extern "C" fn get_properties( - object: *mut zend_object, - ) -> *mut HashTable { - #[inline(always)] - unsafe fn internal( - object: *mut zend_object, - props: &mut HashTable, - ) -> PhpResult { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let self_ = &mut **obj; - let struct_props = T::get_properties(); - - for (name, val) in struct_props.into_iter() { - let mut zv = Zval::new(); - if val.get(self_, &mut zv).is_err() { - continue; - } - props.insert(name, zv).map_err(|e| { - format!("Failed to insert value into properties hashtable: {:?}", e) - })?; - } - - Ok(()) - } - - let props = zend_std_get_properties(object) - .as_mut() - .or_else(|| Some(HashTable::new().into_raw())) - .expect("Failed to get property hashtable"); - - if let Err(e) = internal::(object, props) { - let _ = e.throw(); - } - - props - } - - unsafe extern "C" fn has_property( - object: *mut zend_object, - member: *mut zend_string, - has_set_exists: c_int, - cache_slot: *mut *mut c_void, - ) -> c_int { - #[inline(always)] - unsafe fn internal( - object: *mut zend_object, - member: *mut zend_string, - has_set_exists: c_int, - cache_slot: *mut *mut c_void, - ) -> PhpResult { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; - let props = T::get_properties(); - let prop = props.get(prop_name.as_str().ok_or("Invalid property name given")?); - let self_ = &mut **obj; - - match has_set_exists { - // * 0 (has) whether property exists and is not NULL - 0 => { - if let Some(val) = prop { - let mut zv = Zval::new(); - val.get(self_, &mut zv)?; - if !zv.is_null() { - return Ok(1); - } - } - } - // * 1 (set) whether property exists and is true - 1 => { - if let Some(val) = prop { - let mut zv = Zval::new(); - val.get(self_, &mut zv)?; - - if zend_is_true(&mut zv) == 1 { - return Ok(1); - } - } - } - // * 2 (exists) whether property exists - 2 => { - if prop.is_some() { - return Ok(1); - } - } - _ => return Err( - "Invalid value given for `has_set_exists` in struct `has_property` function." - .into(), - ), - }; - - Ok(zend_std_has_property( - object, - member, - has_set_exists, - cache_slot, - )) - } - - match internal::(object, member, has_set_exists, cache_slot) { - Ok(rv) => rv, - Err(e) => { - let _ = e.throw(); - 0 - } - } - } -} diff --git a/src/php/types/props.rs b/src/props.rs similarity index 98% rename from src/php/types/props.rs rename to src/props.rs index acaa7694b5..0a2813c621 100644 --- a/src/php/types/props.rs +++ b/src/props.rs @@ -1,12 +1,12 @@ //! Utilities for adding properties to classes. use crate::{ - errors::{Error, Result}, - php::exceptions::PhpResult, + convert::{FromZval, IntoZval}, + error::{Error, Result}, + exception::PhpResult, + types::Zval, }; -use super::zval::{FromZval, IntoZval, Zval}; - /// Implemented on types which can be used as PHP properties. /// /// Generally, this should not be directly implemented on types, as it is automatically implemented on diff --git a/src/php/types/rc.rs b/src/rc.rs similarity index 93% rename from src/php/types/rc.rs rename to src/rc.rs index 167ae34fed..689f8f90ba 100644 --- a/src/php/types/rc.rs +++ b/src/rc.rs @@ -1,8 +1,9 @@ //! Utilities for interacting with refcounted PHP types. -use crate::bindings::{zend_refcounted_h, zend_string}; - -use super::object::ZendObject; +use crate::{ + ffi::{zend_refcounted_h, zend_string}, + types::ZendObject, +}; /// Object used to store Zend reference counter. pub type ZendRefcount = zend_refcounted_h; diff --git a/src/php/types/array.rs b/src/types/array.rs similarity index 98% rename from src/php/types/array.rs rename to src/types/array.rs index 0003fe8d7c..5ce9cc44b8 100644 --- a/src/php/types/array.rs +++ b/src/types/array.rs @@ -12,23 +12,21 @@ use std::{ }; use crate::{ - bindings::{ + boxed::{ZBox, ZBoxable}, + convert::{FromZval, IntoZval}, + error::{Error, Result}, + ffi::{ _Bucket, _zend_new_array, zend_array_destroy, zend_array_dup, zend_hash_clean, zend_hash_index_del, zend_hash_index_find, zend_hash_index_update, zend_hash_next_index_insert, zend_hash_str_del, zend_hash_str_find, zend_hash_str_update, HT_MIN_SIZE, }, - errors::{Error, Result}, - php::{ - boxed::{ZBox, ZBoxable}, - enums::DataType, - }, + flags::DataType, + types::Zval, }; -use super::zval::{FromZval, IntoZval, Zval}; - /// PHP array, which is represented in memory as a hashtable. -pub use crate::bindings::HashTable; +pub type HashTable = crate::ffi::HashTable; impl HashTable { /// Creates a new, empty, PHP associative array. diff --git a/src/php/types/callable.rs b/src/types/callable.rs similarity index 87% rename from src/php/types/callable.rs rename to src/types/callable.rs index 5a2f95d148..cadeac5a6a 100644 --- a/src/php/types/callable.rs +++ b/src/types/callable.rs @@ -1,42 +1,21 @@ //! Types related to callables in PHP (anonymous functions, functions, etc). -use std::ops::Deref; +use std::{convert::TryFrom, ops::Deref}; -use super::zval::{IntoZvalDyn, Zval}; use crate::{ - bindings::_call_user_function_impl, - errors::{Error, Result}, + convert::{FromZval, IntoZvalDyn}, + error::{Error, Result}, + ffi::_call_user_function_impl, + flags::DataType, }; +use super::Zval; + /// Acts as a wrapper around a callable [`Zval`]. Allows the owner to call the [`Zval`] as if it /// was a PHP function through the [`try_call`](Callable::try_call) method. #[derive(Debug)] pub struct Callable<'a>(OwnedZval<'a>); -/// A container for a zval. Either contains a reference to a zval or an owned zval. -#[derive(Debug)] -enum OwnedZval<'a> { - Reference(&'a Zval), - Owned(Zval), -} - -impl<'a> OwnedZval<'a> { - fn as_ref(&self) -> &Zval { - match self { - OwnedZval::Reference(zv) => *zv, - OwnedZval::Owned(zv) => zv, - } - } -} - -impl<'a> Deref for OwnedZval<'a> { - type Target = Zval; - - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - impl<'a> Callable<'a> { /// Attempts to create a new [`Callable`] from a zval. /// @@ -123,3 +102,43 @@ impl<'a> Callable<'a> { } } } + +impl<'a> FromZval<'a> for Callable<'a> { + const TYPE: DataType = DataType::Callable; + + fn from_zval(zval: &'a Zval) -> Option { + Callable::new(zval).ok() + } +} + +impl<'a> TryFrom for Callable<'a> { + type Error = Error; + + fn try_from(value: Zval) -> Result { + Callable::new_owned(value) + } +} + +/// A container for a zval. Either contains a reference to a zval or an owned zval. +#[derive(Debug)] +enum OwnedZval<'a> { + Reference(&'a Zval), + Owned(Zval), +} + +impl<'a> OwnedZval<'a> { + fn as_ref(&self) -> &Zval { + match self { + OwnedZval::Reference(zv) => *zv, + OwnedZval::Owned(zv) => zv, + } + } +} + +impl<'a> Deref for OwnedZval<'a> { + type Target = Zval; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} diff --git a/src/types/class_object.rs b/src/types/class_object.rs new file mode 100644 index 0000000000..6316d52a37 --- /dev/null +++ b/src/types/class_object.rs @@ -0,0 +1,295 @@ +//! Represents an object in PHP. Allows for overriding the internal object used by classes, +//! allowing users to store Rust data inside a PHP object. + +use std::{ + fmt::Debug, + mem::{self}, + ops::{Deref, DerefMut}, + ptr::{self, NonNull}, +}; + +use crate::{ + boxed::{ZBox, ZBoxable}, + class::RegisteredClass, + convert::{FromZendObject, FromZendObjectMut, FromZval, FromZvalMut, IntoZval}, + error::{Error, Result}, + ffi::{ + ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init, + zend_object, zend_object_std_init, zend_objects_clone_members, + }, + flags::DataType, + types::{ZendObject, Zval}, +}; + +/// Representation of a Zend class object in memory. +#[repr(C)] +pub struct ZendClassObject { + pub obj: Option, + pub std: ZendObject, +} + +impl ZendClassObject { + /// Creates a new [`ZendClassObject`] of type `T`, where `T` is a [`RegisteredClass`] in PHP, storing the + /// given value `val` inside the object. + /// + /// # Parameters + /// + /// * `val` - The value to store inside the object. + /// + /// # Panics + /// + /// Panics if memory was unable to be allocated for the new object. + pub fn new(val: T) -> ZBox { + unsafe { Self::internal_new(Some(val)) } + } + + /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized internal object. + /// + /// # Safety + /// + /// As the object is uninitialized, the caller must ensure the following until the internal object is + /// initialized: + /// + /// * The object is never dereferenced to `T`. + /// * The [`Clone`] implementation is never called. + /// * The [`Debug`] implementation is never called. + /// + /// If any of these conditions are not met while not initialized, the corresponding function will panic. + /// Converting the object into its inner pointer with the [`into_raw`] function is valid, however. + /// + /// [`into_raw`]: #method.into_raw + /// + /// # Panics + /// + /// Panics if memory was unable to be allocated for the new object. + pub unsafe fn new_uninit() -> ZBox { + Self::internal_new(None) + } + + /// Creates a new [`ZendObject`] of type `T`, storing the given (and potentially uninitialized) `val` + /// inside the object. + /// + /// # Parameters + /// + /// * `val` - Value to store inside the object. See safety section. + /// * `init` - Whether the given `val` was initialized. + /// + /// # Safety + /// + /// Providing an initialized variant of [`MaybeUninit`] is safe. + /// + /// Providing an uninitalized variant of [`MaybeUninit`] is unsafe. As the object is uninitialized, + /// the caller must ensure the following until the internal object is initialized: + /// + /// * The object is never dereferenced to `T`. + /// * The [`Clone`] implementation is never called. + /// * The [`Debug`] implementation is never called. + /// + /// If any of these conditions are not met while not initialized, the corresponding function will panic. + /// Converting the object into its inner with the [`into_raw`] function is valid, however. You can initialize + /// the object with the [`initialize`] function. + /// + /// [`into_raw`]: #method.into_raw + /// [`initialize`]: #method.initialize + /// + /// # Panics + /// + /// Panics if memory was unable to be allocated for the new object. + unsafe fn internal_new(val: Option) -> ZBox { + let size = mem::size_of::>(); + let meta = T::get_metadata(); + let ce = meta.ce() as *const _ as *mut _; + let obj = ext_php_rs_zend_object_alloc(size as _, ce) as *mut ZendClassObject; + let obj = obj + .as_mut() + .expect("Failed to allocate for new Zend object"); + + zend_object_std_init(&mut obj.std, ce); + object_properties_init(&mut obj.std, ce); + + // SAFETY: `obj` is non-null and well aligned as it is a reference. + // As the data in `obj.obj` is uninitalized, we don't want to drop + // the data, but directly override it. + ptr::write(&mut obj.obj, val); + + obj.std.handlers = meta.handlers(); + ZBox::from_raw(obj) + } + + /// Initializes the class object with the value `val`. + /// + /// # Parameters + /// + /// * `val` - The value to initialize the object with. + /// + /// # Returns + /// + /// Returns the old value in an [`Option`] if the object had already been initialized, [`None`] + /// otherwise. + pub fn initialize(&mut self, val: T) -> Option { + self.obj.replace(val) + } + + /// Returns a reference to the [`ZendClassObject`] of a given object `T`. Returns [`None`] + /// if the given object is not of the type `T`. + /// + /// # Parameters + /// + /// * `obj` - The object to get the [`ZendClassObject`] for. + /// + /// # Safety + /// + /// Caller must guarantee that the given `obj` was created by Zend, which means that it + /// is immediately followed by a [`zend_object`]. + pub(crate) unsafe fn from_obj_ptr(obj: &T) -> Option<&mut Self> { + // TODO(david): Remove this function + let ptr = (obj as *const T as *mut Self).as_mut()?; + + if ptr.std.is_instance::() { + Some(ptr) + } else { + None + } + } + + /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. + /// Returns [`None`] if the given object is not of the type `T`. + /// + /// # Parameters + /// + /// * `obj` - The zend object to get the [`ZendClassObject`] for. + pub fn from_zend_obj(std: &zend_object) -> Option<&Self> { + Some(Self::_from_zend_obj(std)?) + } + + /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. + /// Returns [`None`] if the given object is not of the type `T`. + /// + /// # Parameters + /// + /// * `obj` - The zend object to get the [`ZendClassObject`] for. + pub fn from_zend_obj_mut(std: &mut zend_object) -> Option<&mut Self> { + Self::_from_zend_obj(std) + } + + fn _from_zend_obj(std: &zend_object) -> Option<&mut Self> { + let std = std as *const zend_object as *const i8; + let ptr = unsafe { + let ptr = std.offset(0 - Self::std_offset() as isize) as *const Self; + (ptr as *mut Self).as_mut()? + }; + + if ptr.std.is_instance::() { + Some(ptr) + } else { + None + } + } + + /// Returns a mutable reference to the underlying Zend object. + pub fn get_mut_zend_obj(&mut self) -> &mut zend_object { + &mut self.std + } + + /// Returns the offset of the `std` property in the class object. + pub(crate) fn std_offset() -> usize { + unsafe { + let null = NonNull::::dangling(); + let base = null.as_ref() as *const Self; + let std = &null.as_ref().std as *const zend_object; + + (std as usize) - (base as usize) + } + } +} + +impl<'a, T: RegisteredClass> FromZval<'a> for &'a ZendClassObject { + const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); + + fn from_zval(zval: &'a Zval) -> Option { + Self::from_zend_object(zval.object()?).ok() + } +} + +impl<'a, T: RegisteredClass> FromZendObject<'a> for &'a ZendClassObject { + fn from_zend_object(obj: &'a ZendObject) -> Result { + // TODO(david): replace with better error + ZendClassObject::from_zend_obj(obj).ok_or(Error::InvalidScope) + } +} + +impl<'a, T: RegisteredClass> FromZvalMut<'a> for &'a mut ZendClassObject { + const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); + + fn from_zval_mut(zval: &'a mut Zval) -> Option { + Self::from_zend_object_mut(zval.object_mut()?).ok() + } +} + +impl<'a, T: RegisteredClass> FromZendObjectMut<'a> for &'a mut ZendClassObject { + fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result { + ZendClassObject::from_zend_obj_mut(obj).ok_or(Error::InvalidScope) + } +} + +unsafe impl ZBoxable for ZendClassObject { + fn free(&mut self) { + // SAFETY: All constructors guarantee that `self` contains a valid pointer. Further, all constructors + // guarantee that the `std` field of `ZendClassObject` will be initialized. + unsafe { ext_php_rs_zend_object_release(&mut self.std) } + } +} + +impl Deref for ZendClassObject { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.obj + .as_ref() + .expect("Attempted to access uninitalized class object") + } +} + +impl DerefMut for ZendClassObject { + fn deref_mut(&mut self) -> &mut Self::Target { + self.obj + .as_mut() + .expect("Attempted to access uninitalized class object") + } +} + +impl Default for ZBox> { + #[inline] + fn default() -> Self { + ZendClassObject::new(T::default()) + } +} + +impl Clone for ZBox> { + fn clone(&self) -> Self { + // SAFETY: All constructors of `NewClassObject` guarantee that it will contain a valid pointer. + // The constructor also guarantees that the internal `ZendClassObject` pointer will contain a valid, + // initialized `obj`, therefore we can dereference both safely. + unsafe { + let mut new = ZendClassObject::new((&***self).clone()); + zend_objects_clone_members(&mut new.std, &self.std as *const _ as *mut _); + new + } + } +} + +impl Debug for ZendClassObject { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (&**self).fmt(f) + } +} + +impl IntoZval for ZBox> { + const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); + + fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { + let obj = self.into_raw(); + zv.set_object(&mut obj.std); + Ok(()) + } +} diff --git a/src/types/long.rs b/src/types/long.rs new file mode 100644 index 0000000000..5f9389506d --- /dev/null +++ b/src/types/long.rs @@ -0,0 +1,70 @@ +//! Represents an integer introduced in PHP. Note that the size of this integer differs. +//! On a 32-bit system, a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. + +use crate::{ + convert::IntoZval, + error::{Error, Result}, + ffi::zend_long, + flags::DataType, + macros::{into_zval, try_from_zval}, + types::Zval, +}; + +use std::convert::{TryFrom, TryInto}; + +/// Internal identifier used for a long. +/// The size depends on the system architecture. On 32-bit systems, +/// a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. +pub type ZendLong = zend_long; + +into_zval!(i8, set_long, Long); +into_zval!(i16, set_long, Long); +into_zval!(i32, set_long, Long); + +into_zval!(u8, set_long, Long); +into_zval!(u16, set_long, Long); + +macro_rules! try_into_zval_int { + ($type: ty) => { + impl TryFrom<$type> for Zval { + type Error = Error; + + fn try_from(val: $type) -> Result { + let mut zv = Self::new(); + let val: ZendLong = val.try_into().map_err(|_| Error::IntegerOverflow)?; + zv.set_long(val); + Ok(zv) + } + } + + impl IntoZval for $type { + const TYPE: DataType = DataType::Long; + + fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { + let val: ZendLong = self.try_into().map_err(|_| Error::IntegerOverflow)?; + zv.set_long(val); + Ok(()) + } + } + }; +} + +try_into_zval_int!(i64); +try_into_zval_int!(u32); +try_into_zval_int!(u64); + +try_into_zval_int!(isize); +try_into_zval_int!(usize); + +try_from_zval!(i8, long, Long); +try_from_zval!(i16, long, Long); +try_from_zval!(i32, long, Long); +try_from_zval!(i64, long, Long); + +try_from_zval!(u8, long, Long); +try_from_zval!(u16, long, Long); +try_from_zval!(u32, long, Long); +try_from_zval!(u64, long, Long); + +try_from_zval!(usize, long, Long); +try_from_zval!(isize, long, Long); diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000000..4a728d635d --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,32 @@ +mod array; +mod callable; +mod class_object; +mod long; +mod object; +mod string; +mod zval; + +pub use array::HashTable; +pub use callable::Callable; +pub use class_object::ZendClassObject; +pub use long::ZendLong; +pub use object::ZendObject; +pub use string::ZendStr; +pub use zval::Zval; + +use crate::{convert::FromZval, flags::DataType, macros::into_zval}; + +into_zval!(f32, set_double, Double); +into_zval!(f64, set_double, Double); +into_zval!(bool, set_bool, Bool); + +try_from_zval!(f64, double, Double); +try_from_zval!(bool, bool, Bool); + +impl FromZval<'_> for f32 { + const TYPE: DataType = DataType::Double; + + fn from_zval(zval: &Zval) -> Option { + zval.double().map(|v| v as f32) + } +} diff --git a/src/types/object.rs b/src/types/object.rs new file mode 100644 index 0000000000..3490c6d59e --- /dev/null +++ b/src/types/object.rs @@ -0,0 +1,297 @@ +//! Represents an object in PHP. Allows for overriding the internal object used by classes, +//! allowing users to store Rust data inside a PHP object. + +use std::{convert::TryInto, fmt::Debug, ops::DerefMut}; + +use crate::{ + boxed::{ZBox, ZBoxable}, + class::RegisteredClass, + convert::{FromZendObject, FromZval, IntoZval}, + error::{Error, Result}, + ffi::{ + ext_php_rs_zend_object_release, zend_call_known_function, zend_object, zend_objects_new, + zend_standard_class_def, HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, + ZEND_PROPERTY_ISSET, + }, + flags::DataType, + rc::PhpRc, + types::{ZendClassObject, ZendStr, Zval}, + zend::{ClassEntry, ExecutorGlobals, ZendObjectHandlers}, +}; + +pub type ZendObject = zend_object; + +impl ZendObject { + /// Creates a new [`ZendObject`], returned inside an [`ZBox`] wrapper. + /// + /// # Parameters + /// + /// * `ce` - The type of class the new object should be an instance of. + /// + /// # Panics + /// + /// Panics when allocating memory for the new object fails. + pub fn new(ce: &ClassEntry) -> ZBox { + // SAFETY: Using emalloc to allocate memory inside Zend arena. Casting `ce` to `*mut` is valid + // as the function will not mutate `ce`. + unsafe { + let ptr = zend_objects_new(ce as *const _ as *mut _); + ZBox::from_raw( + ptr.as_mut() + .expect("Failed to allocate memory for Zend object"), + ) + } + } + + /// Creates a new `stdClass` instance, returned inside an [`ZBox`] wrapper. + /// + /// # Panics + /// + /// Panics if allocating memory for the object fails, or if the `stdClass` class entry has not been + /// registered with PHP yet. + pub fn new_stdclass() -> ZBox { + // SAFETY: This will be `NULL` until it is initialized. `as_ref()` checks for null, + // so we can panic if it's null. + Self::new(unsafe { + zend_standard_class_def + .as_ref() + .expect("`stdClass` class instance not initialized yet") + }) + } + + /// Converts the class object into an owned [`ZendObject`]. This removes any possibility of + /// accessing the underlying attached Rust struct. + pub fn from_class_object(obj: ZBox>) -> ZBox { + let this = obj.into_raw(); + // SAFETY: Consumed box must produce a well-aligned non-null pointer. + unsafe { ZBox::from_raw(this.get_mut_zend_obj()) } + } + + /// Attempts to retrieve the class name of the object. + pub fn get_class_name(&self) -> Result { + unsafe { + self.handlers()? + .get_class_name + .and_then(|f| f(self).as_ref()) + .ok_or(Error::InvalidScope) + .and_then(|s| s.try_into()) + } + } + + /// Checks if the given object is an instance of a registered class with Rust + /// type `T`. + pub fn is_instance(&self) -> bool { + (self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _)) + } + + /// Attempts to read a property from the Object. Returns a result containing the + /// value of the property if it exists and can be read, and an [`Error`] otherwise. + /// + /// # Parameters + /// + /// * `name` - The name of the property. + /// * `query` - The type of query to use when attempting to get a property. + pub fn get_property<'a, T>(&'a self, name: &str) -> Result + where + T: FromZval<'a>, + { + if !self.has_property(name, PropertyQuery::Exists)? { + return Err(Error::InvalidProperty); + } + + let mut name = ZendStr::new(name, false)?; + let mut rv = Zval::new(); + + let zv = unsafe { + self.handlers()?.read_property.ok_or(Error::InvalidScope)?( + self.mut_ptr(), + name.deref_mut(), + 1, + std::ptr::null_mut(), + &mut rv, + ) + .as_ref() + } + .ok_or(Error::InvalidScope)?; + + T::from_zval(zv).ok_or_else(|| Error::ZvalConversion(zv.get_type())) + } + + /// Attempts to set a property on the object. + /// + /// # Parameters + /// + /// * `name` - The name of the property. + /// * `value` - The value to set the property to. + pub fn set_property(&mut self, name: &str, value: impl IntoZval) -> Result<()> { + let mut name = ZendStr::new(name, false)?; + let mut value = value.into_zval(false)?; + + unsafe { + self.handlers()?.write_property.ok_or(Error::InvalidScope)?( + self, + name.deref_mut(), + &mut value, + std::ptr::null_mut(), + ) + .as_ref() + } + .ok_or(Error::InvalidScope)?; + Ok(()) + } + + /// Checks if a property exists on an object. Takes a property name and query parameter, + /// which defines what classifies if a property exists or not. See [`PropertyQuery`] for + /// more information. + /// + /// # Parameters + /// + /// * `name` - The name of the property. + /// * `query` - The 'query' to classify if a property exists. + pub fn has_property(&self, name: &str, query: PropertyQuery) -> Result { + let mut name = ZendStr::new(name, false)?; + + Ok(unsafe { + self.handlers()?.has_property.ok_or(Error::InvalidScope)?( + self.mut_ptr(), + name.deref_mut(), + query as _, + std::ptr::null_mut(), + ) + } > 0) + } + + /// Attempts to retrieve the properties of the object. Returned inside a Zend Hashtable. + pub fn get_properties(&self) -> Result<&HashTable> { + unsafe { + self.handlers()? + .get_properties + .and_then(|props| props(self.mut_ptr()).as_ref()) + .ok_or(Error::InvalidScope) + } + } + + /// Attempts to retrieve a reference to the object handlers. + #[inline] + unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> { + self.handlers.as_ref().ok_or(Error::InvalidScope) + } + + /// Returns a mutable pointer to `self`, regardless of the type of reference. + /// Only to be used in situations where a C function requires a mutable pointer + /// but does not modify the underlying data. + #[inline] + fn mut_ptr(&self) -> *mut Self { + (self as *const Self) as *mut Self + } + + /// Extracts some type from a Zend object. + /// + /// This is a wrapper function around `FromZendObject::extract()`. + pub fn extract<'a, T>(&'a self) -> Result + where + T: FromZendObject<'a>, + { + T::from_zend_object(self) + } +} + +unsafe impl ZBoxable for ZendObject { + fn free(&mut self) { + unsafe { ext_php_rs_zend_object_release(self) } + } +} + +impl Debug for ZendObject { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut dbg = f.debug_struct( + self.get_class_name() + .unwrap_or_else(|_| "ZendObject".to_string()) + .as_str(), + ); + + if let Ok(props) = self.get_properties() { + for (id, key, val) in props.iter() { + dbg.field(key.unwrap_or_else(|| id.to_string()).as_str(), val); + } + } + + dbg.finish() + } +} + +impl<'a> FromZval<'a> for &'a ZendObject { + const TYPE: DataType = DataType::Object(None); + + fn from_zval(zval: &'a Zval) -> Option { + zval.object() + } +} + +impl IntoZval for ZBox { + const TYPE: DataType = DataType::Object(None); + + fn set_zval(mut self, zv: &mut Zval, _: bool) -> Result<()> { + // We must decrement the refcounter on the object before inserting into the zval, + // as the reference counter will be incremented on add. + // NOTE(david): again is this needed, we increment in `set_object`. + self.dec_count(); + zv.set_object(self.into_raw()); + Ok(()) + } +} + +impl FromZendObject<'_> for String { + fn from_zend_object(obj: &ZendObject) -> Result { + let mut ret = Zval::new(); + unsafe { + zend_call_known_function( + (*obj.ce).__tostring, + obj as *const _ as *mut _, + obj.ce, + &mut ret, + 0, + std::ptr::null_mut(), + std::ptr::null_mut(), + ); + } + + if let Some(err) = ExecutorGlobals::take_exception() { + // TODO: become an error + let class_name = obj.get_class_name(); + panic!( + "Uncaught exception during call to {}::__toString(): {:?}", + class_name.expect("unable to determine class name"), + err + ); + } else if let Some(output) = ret.extract() { + Ok(output) + } else { + // TODO: become an error + let class_name = obj.get_class_name(); + panic!( + "{}::__toString() must return a string", + class_name.expect("unable to determine class name"), + ); + } + } +} + +impl From>> for ZBox { + #[inline] + fn from(obj: ZBox>) -> Self { + ZendObject::from_class_object(obj) + } +} + +/// Different ways to query if a property exists. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u32)] +pub enum PropertyQuery { + /// Property exists and is not NULL. + Isset = ZEND_PROPERTY_ISSET, + /// Property is not empty. + NotEmpty = ZEND_ISEMPTY, + /// Property exists. + Exists = ZEND_PROPERTY_EXISTS, +} diff --git a/src/php/types/string.rs b/src/types/string.rs similarity index 89% rename from src/php/types/string.rs rename to src/types/string.rs index b44403417e..ab37b35530 100644 --- a/src/php/types/string.rs +++ b/src/types/string.rs @@ -15,12 +15,16 @@ use parking_lot::{ }; use crate::{ - bindings::{ + boxed::{ZBox, ZBoxable}, + convert::{FromZval, IntoZval}, + error::{Error, Result}, + ffi::{ ext_php_rs_zend_string_init, ext_php_rs_zend_string_release, zend_string, zend_string_init_interned, }, - errors::{Error, Result}, - php::boxed::{ZBox, ZBoxable}, + flags::DataType, + macros::try_from_zval, + types::Zval, }; /// A borrowed Zend-string. @@ -268,3 +272,37 @@ impl From> for ZBox { value.into_owned() } } + +macro_rules! try_into_zval_str { + ($type: ty) => { + impl TryFrom<$type> for Zval { + type Error = Error; + + fn try_from(value: $type) -> Result { + let mut zv = Self::new(); + zv.set_string(&value, false)?; + Ok(zv) + } + } + + impl IntoZval for $type { + const TYPE: DataType = DataType::String; + + fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { + zv.set_string(&self, persistent) + } + } + }; +} + +try_into_zval_str!(String); +try_into_zval_str!(&str); +try_from_zval!(String, string, String); + +impl<'a> FromZval<'a> for &'a str { + const TYPE: DataType = DataType::String; + + fn from_zval(zval: &'a Zval) -> Option { + zval.str() + } +} diff --git a/src/php/types/zval.rs b/src/types/zval.rs similarity index 65% rename from src/php/types/zval.rs rename to src/types/zval.rs index 5905c1acdf..a7ca614c2b 100644 --- a/src/php/types/zval.rs +++ b/src/types/zval.rs @@ -1,26 +1,23 @@ //! The base value in PHP. A Zval can contain any PHP type, and the type that it contains is //! determined by a property inside the struct. The content of the Zval is stored in a union. -use std::{ - convert::{TryFrom, TryInto}, - ffi::c_void, - fmt::Debug, - ptr, -}; +use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr}; use crate::{ - bindings::{ + binary::Pack, + boxed::ZBox, + convert::{FromZval, IntoZval, IntoZvalDyn}, + error::{Error, Result}, + ffi::{ _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2, zend_is_callable, zend_resource, zend_value, zval, zval_ptr_dtor, }, - errors::{Error, Result}, - php::{boxed::ZBox, exceptions::PhpException, pack::Pack}, + flags::DataType, + flags::ZvalTypeFlags, + rc::PhpRc, + types::{Callable, HashTable, ZendLong, ZendObject, ZendStr}, }; -use crate::php::{enums::DataType, flags::ZvalTypeFlags, types::long::ZendLong}; - -use super::{array::HashTable, callable::Callable, object::ZendObject, rc::PhpRc, string::ZendStr}; - /// Zend value. Represents most data types that are in the Zend engine. pub type Zval = zval; @@ -520,35 +517,6 @@ impl Default for Zval { } } -/// Provides implementations for converting Rust primitive types into PHP zvals. Alternative to the -/// built-in Rust [`From`] and [`TryFrom`] implementations, allowing the caller to specify whether -/// the Zval contents will persist between requests. -pub trait IntoZval: Sized { - /// The corresponding type of the implemented value in PHP. - const TYPE: DataType; - - /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if - /// successful. - /// - /// # Parameters - /// - /// * `persistent` - Whether the contents of the Zval will persist between requests. - fn into_zval(self, persistent: bool) -> Result { - let mut zval = Zval::new(); - self.set_zval(&mut zval, persistent)?; - Ok(zval) - } - - /// Sets the content of a pre-existing zval. Returns a result containing nothing if setting - /// the content was successful. - /// - /// # Parameters - /// - /// * `zv` - The Zval to set the content of. - /// * `persistent` - Whether the contents of the Zval will persist between requests. - fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()>; -} - impl IntoZval for Zval { const TYPE: DataType = DataType::Mixed; @@ -557,287 +525,3 @@ impl IntoZval for Zval { Ok(()) } } - -/// An object-safe version of the [`IntoZval`] trait. -/// -/// This trait is automatically implemented on any type that implements both [`IntoZval`] and [`Clone`]. -/// You avoid implementing this trait directly, rather implement these two other traits. -pub trait IntoZvalDyn { - /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if - /// successful. `self` is cloned before being converted into a zval. - /// - /// # Parameters - /// - /// * `persistent` - Whether the contents of the Zval will persist between requests. - fn as_zval(&self, persistent: bool) -> Result; - - /// Returns the PHP type of the type. - fn get_type(&self) -> DataType; -} - -impl IntoZvalDyn for T { - fn as_zval(&self, persistent: bool) -> Result { - self.clone().into_zval(persistent) - } - - fn get_type(&self) -> DataType { - Self::TYPE - } -} - -macro_rules! into_zval { - ($type: ty, $fn: ident, $dt: ident) => { - impl From<$type> for Zval { - fn from(val: $type) -> Self { - let mut zv = Self::new(); - zv.$fn(val); - zv - } - } - - impl IntoZval for $type { - const TYPE: DataType = DataType::$dt; - - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - zv.$fn(self); - Ok(()) - } - } - }; -} - -into_zval!(i8, set_long, Long); -into_zval!(i16, set_long, Long); -into_zval!(i32, set_long, Long); - -into_zval!(u8, set_long, Long); -into_zval!(u16, set_long, Long); - -into_zval!(f32, set_double, Double); -into_zval!(f64, set_double, Double); - -into_zval!(bool, set_bool, Bool); - -macro_rules! try_into_zval_int { - ($type: ty) => { - impl TryFrom<$type> for Zval { - type Error = Error; - - fn try_from(val: $type) -> Result { - let mut zv = Self::new(); - let val: ZendLong = val.try_into().map_err(|_| Error::IntegerOverflow)?; - zv.set_long(val); - Ok(zv) - } - } - - impl IntoZval for $type { - const TYPE: DataType = DataType::Long; - - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - let val: ZendLong = self.try_into().map_err(|_| Error::IntegerOverflow)?; - zv.set_long(val); - Ok(()) - } - } - }; -} - -try_into_zval_int!(i64); -try_into_zval_int!(u32); -try_into_zval_int!(u64); - -try_into_zval_int!(isize); -try_into_zval_int!(usize); - -macro_rules! try_into_zval_str { - ($type: ty) => { - impl TryFrom<$type> for Zval { - type Error = Error; - - fn try_from(value: $type) -> Result { - let mut zv = Self::new(); - zv.set_string(&value, false)?; - Ok(zv) - } - } - - impl IntoZval for $type { - const TYPE: DataType = DataType::String; - - fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { - zv.set_string(&self, persistent) - } - } - }; -} - -try_into_zval_str!(String); -try_into_zval_str!(&str); - -impl IntoZval for () { - const TYPE: DataType = DataType::Void; - - fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { - zv.set_null(); - Ok(()) - } -} - -impl IntoZval for Option -where - T: IntoZval, -{ - const TYPE: DataType = T::TYPE; - - fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { - match self { - Some(val) => val.set_zval(zv, persistent), - None => { - zv.set_null(); - Ok(()) - } - } - } -} - -impl IntoZval for std::result::Result -where - T: IntoZval, - E: Into, -{ - const TYPE: DataType = T::TYPE; - - fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> { - match self { - Ok(val) => val.set_zval(zv, persistent), - Err(e) => { - let ex: PhpException = e.into(); - ex.throw() - } - } - } -} - -/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal of the [`IntoZval`] -/// trait. -pub trait FromZval<'a>: Sized { - /// The corresponding type of the implemented value in PHP. - const TYPE: DataType; - - /// Attempts to retrieve an instance of `Self` from a reference to a [`Zval`]. - /// - /// # Parameters - /// - /// * `zval` - Zval to get value from. - fn from_zval(zval: &'a Zval) -> Option; -} - -/// Allows mutable zvals to be converted into Rust types in a fallible way. -/// -/// If `Self` does not require the zval to be mutable to be extracted, you should implement -/// [`FromZval`] instead, as this trait is generically implemented for any type that implements -/// [`FromZval`]. -pub trait FromZvalMut<'a>: Sized { - /// The corresponding type of the implemented value in PHP. - const TYPE: DataType; - - /// Attempts to retrieve an instance of `Self` from a mutable reference to a [`Zval`]. - /// - /// # Parameters - /// - /// * `zval` - Zval to get value from. - fn from_zval_mut(zval: &'a mut Zval) -> Option; -} - -impl<'a, T> FromZvalMut<'a> for T -where - T: FromZval<'a>, -{ - const TYPE: DataType = ::TYPE; - - #[inline] - fn from_zval_mut(zval: &'a mut Zval) -> Option { - Self::from_zval(zval) - } -} - -impl<'a, T> FromZval<'a> for Option -where - T: FromZval<'a>, -{ - const TYPE: DataType = T::TYPE; - - fn from_zval(zval: &'a Zval) -> Option { - Some(T::from_zval(zval)) - } -} - -macro_rules! try_from_zval { - ($type: ty, $fn: ident, $dt: ident) => { - impl FromZval<'_> for $type { - const TYPE: DataType = DataType::$dt; - - fn from_zval(zval: &Zval) -> Option { - zval.$fn().and_then(|val| val.try_into().ok()) - } - } - - impl TryFrom for $type { - type Error = Error; - - fn try_from(value: Zval) -> Result { - Self::from_zval(&value).ok_or(Error::ZvalConversion(value.get_type())) - } - } - }; -} - -try_from_zval!(i8, long, Long); -try_from_zval!(i16, long, Long); -try_from_zval!(i32, long, Long); -try_from_zval!(i64, long, Long); - -try_from_zval!(u8, long, Long); -try_from_zval!(u16, long, Long); -try_from_zval!(u32, long, Long); -try_from_zval!(u64, long, Long); - -try_from_zval!(usize, long, Long); -try_from_zval!(isize, long, Long); - -try_from_zval!(f64, double, Double); -try_from_zval!(bool, bool, Bool); -try_from_zval!(String, string, String); - -impl FromZval<'_> for f32 { - const TYPE: DataType = DataType::Double; - - fn from_zval(zval: &Zval) -> Option { - zval.double().map(|v| v as f32) - } -} - -impl<'a> FromZval<'a> for &'a str { - const TYPE: DataType = DataType::String; - - fn from_zval(zval: &'a Zval) -> Option { - zval.str() - } -} - -impl<'a> FromZval<'a> for Callable<'a> { - const TYPE: DataType = DataType::Callable; - - fn from_zval(zval: &'a Zval) -> Option { - Callable::new(zval).ok() - } -} - -impl<'a> TryFrom for Callable<'a> { - type Error = Error; - - fn try_from(value: Zval) -> Result { - Callable::new_owned(value) - } -} diff --git a/src/wrapper/wrapper.c b/src/wrapper.c similarity index 100% rename from src/wrapper/wrapper.c rename to src/wrapper.c diff --git a/src/wrapper/wrapper.h b/src/wrapper.h similarity index 100% rename from src/wrapper/wrapper.h rename to src/wrapper.h diff --git a/src/php/types/mod.rs b/src/zend/_type.rs similarity index 89% rename from src/php/types/mod.rs rename to src/zend/_type.rs index 1dfba576b3..7ca2d2bd12 100644 --- a/src/php/types/mod.rs +++ b/src/zend/_type.rs @@ -1,31 +1,16 @@ -//! Contains all the different types that are introduced into PHP. -//! Introduces functions for converting between Zend values and Rust values. - -pub mod array; -pub mod binary; -pub mod callable; -#[cfg(any(docs, feature = "closure"))] -#[cfg_attr(docs, doc(cfg(feature = "closure")))] -pub mod closure; -pub mod long; -pub mod object; -pub mod props; -pub mod rc; -pub mod string; -pub mod zval; - use std::{ ffi::{c_void, CString}, ptr, }; -use crate::bindings::{ - zend_type, IS_MIXED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL, _ZEND_IS_VARIADIC_BIT, - _ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NAME_BIT, _ZEND_TYPE_NULLABLE_BIT, +use crate::{ + ffi::{ + zend_type, IS_MIXED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL, _ZEND_IS_VARIADIC_BIT, + _ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NAME_BIT, _ZEND_TYPE_NULLABLE_BIT, + }, + flags::DataType, }; -use super::enums::DataType; - /// Internal Zend type. pub type ZendType = zend_type; diff --git a/src/zend/ce.rs b/src/zend/ce.rs new file mode 100644 index 0000000000..73df41c6ce --- /dev/null +++ b/src/zend/ce.rs @@ -0,0 +1,65 @@ +#![allow(clippy::unwrap_used)] + +use crate::ffi::{ + zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_compile_error, + zend_ce_division_by_zero_error, zend_ce_error_exception, zend_ce_exception, + zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error, + zend_ce_value_error, +}; + +use super::ClassEntry; + +/// Returns the base `Throwable` class. +pub fn throwable() -> &'static ClassEntry { + unsafe { zend_ce_throwable.as_ref() }.unwrap() +} + +/// Returns the base `Exception` class. +pub fn exception() -> &'static ClassEntry { + unsafe { zend_ce_exception.as_ref() }.unwrap() +} + +/// Returns the base `ErrorException` class. +pub fn error_exception() -> &'static ClassEntry { + unsafe { zend_ce_error_exception.as_ref() }.unwrap() +} + +/// Returns the base `CompileError` class. +pub fn compile_error() -> &'static ClassEntry { + unsafe { zend_ce_compile_error.as_ref() }.unwrap() +} + +/// Returns the base `ParseError` class. +pub fn parse_error() -> &'static ClassEntry { + unsafe { zend_ce_parse_error.as_ref() }.unwrap() +} + +/// Returns the base `TypeError` class. +pub fn type_error() -> &'static ClassEntry { + unsafe { zend_ce_type_error.as_ref() }.unwrap() +} + +/// Returns the base `ArgumentCountError` class. +pub fn argument_count_error() -> &'static ClassEntry { + unsafe { zend_ce_argument_count_error.as_ref() }.unwrap() +} + +/// Returns the base `ValueError` class. +pub fn value_error() -> &'static ClassEntry { + unsafe { zend_ce_value_error.as_ref() }.unwrap() +} + +/// Returns the base `ArithmeticError` class. +pub fn arithmetic_error() -> &'static ClassEntry { + unsafe { zend_ce_arithmetic_error.as_ref() }.unwrap() +} + +/// Returns the base `DivisionByZeroError` class. +pub fn division_by_zero_error() -> &'static ClassEntry { + unsafe { zend_ce_division_by_zero_error.as_ref() }.unwrap() +} + +/// Returns the base `UnhandledMatchError` class. +pub fn unhandled_match_error() -> &'static ClassEntry { + unsafe { zend_ce_unhandled_match_error.as_ref() }.unwrap() +} diff --git a/src/zend/class.rs b/src/zend/class.rs new file mode 100644 index 0000000000..909398addc --- /dev/null +++ b/src/zend/class.rs @@ -0,0 +1,121 @@ +//! Builder and objects for creating classes in the PHP world. + +use crate::{ffi::zend_class_entry, flags::ClassFlags, types::ZendStr, zend::ExecutorGlobals}; +use std::{convert::TryInto, fmt::Debug, ops::DerefMut}; + +/// A Zend class entry. Alias. +pub type ClassEntry = zend_class_entry; + +impl PartialEq for ClassEntry { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self, other) + } +} + +impl ClassEntry { + /// Attempts to find a reference to a class in the global class table. + /// + /// Returns a reference to the class if found, or [`None`] if the class could + /// not be found or the class table has not been initialized. + pub fn try_find(name: &str) -> Option<&'static Self> { + ExecutorGlobals::get().class_table()?; + let mut name = ZendStr::new(name, false).ok()?; + + unsafe { + crate::ffi::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0).as_ref() + } + } + + /// Returns the class flags. + pub fn flags(&self) -> ClassFlags { + ClassFlags::from_bits_truncate(self.ce_flags) + } + + /// Returns `true` if the class entry is an interface, and `false` otherwise. + pub fn is_interface(&self) -> bool { + self.flags().contains(ClassFlags::Interface) + } + + /// Checks if the class is an instance of another class or interface. + /// + /// # Parameters + /// + /// * `ce` - The inherited class entry to check. + pub fn instance_of(&self, ce: &ClassEntry) -> bool { + if self == ce { + return true; + } + + if ce.flags().contains(ClassFlags::Interface) { + let interfaces = match self.interfaces() { + Some(interfaces) => interfaces, + None => return false, + }; + + for i in interfaces { + if ce == i { + return true; + } + } + } else { + loop { + let parent = match self.parent() { + Some(parent) => parent, + None => return false, + }; + + if parent == ce { + return true; + } + } + } + + false + } + + /// Returns an iterator of all the interfaces that the class implements. Returns [`None`] if + /// the interfaces have not been resolved on the class. + pub fn interfaces(&self) -> Option> { + self.flags() + .contains(ClassFlags::ResolvedInterfaces) + .then(|| unsafe { + (0..self.num_interfaces) + .into_iter() + .map(move |i| *self.__bindgen_anon_3.interfaces.offset(i as _)) + .filter_map(|ptr| ptr.as_ref()) + }) + } + + /// Returns the parent of the class. + /// + /// If the parent of the class has not been resolved, it attempts to find the parent by name. + /// Returns [`None`] if the parent was not resolved and the parent was not able to be found + /// by name. + pub fn parent(&self) -> Option<&Self> { + if self.flags().contains(ClassFlags::ResolvedParent) { + unsafe { self.__bindgen_anon_1.parent.as_ref() } + } else { + let name = unsafe { self.__bindgen_anon_1.parent_name.as_ref()? }; + Self::try_find(name.as_str()?) + } + } +} + +impl Debug for ClassEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name: String = unsafe { self.name.as_ref() } + .and_then(|s| s.try_into().ok()) + .ok_or(std::fmt::Error)?; + + f.debug_struct("ClassEntry") + .field("name", &name) + .field("flags", &self.flags()) + .field("is_interface", &self.is_interface()) + .field( + "interfaces", + &self.interfaces().map(|iter| iter.collect::>()), + ) + .field("parent", &self.parent()) + .finish() + } +} diff --git a/src/php/execution_data.rs b/src/zend/ex.rs similarity index 96% rename from src/php/execution_data.rs rename to src/zend/ex.rs index 24bdeca8a5..0f569c8d73 100644 --- a/src/php/execution_data.rs +++ b/src/zend/ex.rs @@ -1,14 +1,12 @@ //! Functions for interacting with the execution data passed to PHP functions\ //! introduced in Rust. -use crate::bindings::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; +use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; -use super::{ +use crate::{ args::ArgParser, - types::{ - object::{RegisteredClass, ZendClassObject, ZendObject}, - zval::Zval, - }, + class::RegisteredClass, + types::{ZendClassObject, ZendObject, Zval}, }; /// Execution data passed when a function is called from Zend. diff --git a/src/zend/function.rs b/src/zend/function.rs new file mode 100644 index 0000000000..8088682c18 --- /dev/null +++ b/src/zend/function.rs @@ -0,0 +1,26 @@ +//! Builder and objects used to create functions and methods in PHP. + +use std::{os::raw::c_char, ptr}; + +use crate::ffi::zend_function_entry; + +/// A Zend function entry. Alias. +pub type FunctionEntry = zend_function_entry; + +impl FunctionEntry { + /// Returns an empty function entry, signifing the end of a function list. + pub fn end() -> Self { + Self { + fname: ptr::null() as *const c_char, + handler: None, + arg_info: ptr::null(), + num_args: 0, + flags: 0, + } + } + + /// Converts the function entry into a raw and pointer, releasing it to the C world. + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} diff --git a/src/php/globals.rs b/src/zend/globals.rs similarity index 92% rename from src/php/globals.rs rename to src/zend/globals.rs index ee241a9eff..c12381a198 100644 --- a/src/php/globals.rs +++ b/src/zend/globals.rs @@ -1,8 +1,8 @@ //! Types related to the PHP executor globals. -use crate::bindings::{_zend_executor_globals, ext_php_rs_executor_globals}; +use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals}; -use super::types::{array::HashTable, object::ZendObject}; +use crate::types::{HashTable, ZendObject}; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs new file mode 100644 index 0000000000..3ff3fd62b3 --- /dev/null +++ b/src/zend/handlers.rs @@ -0,0 +1,257 @@ +use std::{ffi::c_void, os::raw::c_int, ptr}; + +use crate::{ + class::RegisteredClass, + exception::PhpResult, + ffi::{ + std_object_handlers, zend_is_true, zend_object_handlers, zend_object_std_dtor, + zend_std_get_properties, zend_std_has_property, zend_std_read_property, + zend_std_write_property, + }, + flags::ZvalTypeFlags, + types::{HashTable, ZendClassObject, ZendObject, ZendStr, Zval}, +}; + +pub type ZendObjectHandlers = zend_object_handlers; + +impl ZendObjectHandlers { + /// Initializes a given set of object handlers by copying the standard object handlers into + /// the memory location, as well as setting up the `T` type destructor. + /// + /// # Parameters + /// + /// * `ptr` - Pointer to memory location to copy the standard handlers to. + /// + /// # Safety + /// + /// Caller must guarantee that the `ptr` given is a valid memory location. + pub unsafe fn init(ptr: *mut ZendObjectHandlers) { + std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1); + let offset = ZendClassObject::::std_offset(); + (*ptr).offset = offset as _; + (*ptr).free_obj = Some(Self::free_obj::); + (*ptr).read_property = Some(Self::read_property::); + (*ptr).write_property = Some(Self::write_property::); + (*ptr).get_properties = Some(Self::get_properties::); + (*ptr).has_property = Some(Self::has_property::); + } + + unsafe extern "C" fn free_obj(object: *mut ZendObject) { + let obj = object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .expect("Invalid object pointer given for `free_obj`"); + + // Manually drop the object as we don't want to free the underlying memory. + ptr::drop_in_place(&mut **obj); + + zend_object_std_dtor(object) + } + + unsafe extern "C" fn read_property( + object: *mut ZendObject, + member: *mut ZendStr, + type_: c_int, + cache_slot: *mut *mut c_void, + rv: *mut Zval, + ) -> *mut Zval { + #[inline(always)] + unsafe fn internal( + object: *mut ZendObject, + member: *mut ZendStr, + type_: c_int, + cache_slot: *mut *mut c_void, + rv: *mut Zval, + ) -> PhpResult<*mut Zval> { + let obj = object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")?; + let prop_name = member + .as_ref() + .ok_or("Invalid property name pointer given")?; + let self_ = &mut **obj; + let mut props = T::get_properties(); + let prop = props.remove(prop_name.as_str().ok_or("Invalid property name given")?); + + // retval needs to be treated as initialized, so we set the type to null + let rv_mut = rv.as_mut().ok_or("Invalid return zval given")?; + rv_mut.u1.type_info = ZvalTypeFlags::Null.bits(); + + Ok(match prop { + Some(prop) => { + prop.get(self_, rv_mut)?; + rv + } + None => zend_std_read_property(object, member, type_, cache_slot, rv), + }) + } + + match internal::(object, member, type_, cache_slot, rv) { + Ok(rv) => rv, + Err(e) => { + let _ = e.throw(); + (&mut *rv).set_null(); + rv + } + } + } + + unsafe extern "C" fn write_property( + object: *mut ZendObject, + member: *mut ZendStr, + value: *mut Zval, + cache_slot: *mut *mut c_void, + ) -> *mut Zval { + #[inline(always)] + unsafe fn internal( + object: *mut ZendObject, + member: *mut ZendStr, + value: *mut Zval, + cache_slot: *mut *mut c_void, + ) -> PhpResult<*mut Zval> { + let obj = object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")?; + let prop_name = member + .as_ref() + .ok_or("Invalid property name pointer given")?; + let self_ = &mut **obj; + let mut props = T::get_properties(); + let prop = props.remove(prop_name.as_str().ok_or("Invalid property name given")?); + let value_mut = value.as_mut().ok_or("Invalid return zval given")?; + + Ok(match prop { + Some(prop) => { + prop.set(self_, value_mut)?; + value + } + None => zend_std_write_property(object, member, value, cache_slot), + }) + } + + match internal::(object, member, value, cache_slot) { + Ok(rv) => rv, + Err(e) => { + let _ = e.throw(); + value + } + } + } + + unsafe extern "C" fn get_properties( + object: *mut ZendObject, + ) -> *mut HashTable { + #[inline(always)] + unsafe fn internal( + object: *mut ZendObject, + props: &mut HashTable, + ) -> PhpResult { + let obj = object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")?; + let self_ = &mut **obj; + let struct_props = T::get_properties(); + + for (name, val) in struct_props.into_iter() { + let mut zv = Zval::new(); + if val.get(self_, &mut zv).is_err() { + continue; + } + props.insert(name, zv).map_err(|e| { + format!("Failed to insert value into properties hashtable: {:?}", e) + })?; + } + + Ok(()) + } + + let props = zend_std_get_properties(object) + .as_mut() + .or_else(|| Some(HashTable::new().into_raw())) + .expect("Failed to get property hashtable"); + + if let Err(e) = internal::(object, props) { + let _ = e.throw(); + } + + props + } + + unsafe extern "C" fn has_property( + object: *mut ZendObject, + member: *mut ZendStr, + has_set_exists: c_int, + cache_slot: *mut *mut c_void, + ) -> c_int { + #[inline(always)] + unsafe fn internal( + object: *mut ZendObject, + member: *mut ZendStr, + has_set_exists: c_int, + cache_slot: *mut *mut c_void, + ) -> PhpResult { + let obj = object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")?; + let prop_name = member + .as_ref() + .ok_or("Invalid property name pointer given")?; + let props = T::get_properties(); + let prop = props.get(prop_name.as_str().ok_or("Invalid property name given")?); + let self_ = &mut **obj; + + match has_set_exists { + // * 0 (has) whether property exists and is not NULL + 0 => { + if let Some(val) = prop { + let mut zv = Zval::new(); + val.get(self_, &mut zv)?; + if !zv.is_null() { + return Ok(1); + } + } + } + // * 1 (set) whether property exists and is true + 1 => { + if let Some(val) = prop { + let mut zv = Zval::new(); + val.get(self_, &mut zv)?; + + if zend_is_true(&mut zv) == 1 { + return Ok(1); + } + } + } + // * 2 (exists) whether property exists + 2 => { + if prop.is_some() { + return Ok(1); + } + } + _ => return Err( + "Invalid value given for `has_set_exists` in struct `has_property` function." + .into(), + ), + }; + + Ok(zend_std_has_property( + object, + member, + has_set_exists, + cache_slot, + )) + } + + match internal::(object, member, has_set_exists, cache_slot) { + Ok(rv) => rv, + Err(e) => { + let _ = e.throw(); + 0 + } + } + } +} diff --git a/src/zend/mod.rs b/src/zend/mod.rs new file mode 100644 index 0000000000..4a86683585 --- /dev/null +++ b/src/zend/mod.rs @@ -0,0 +1,16 @@ +mod _type; +pub mod ce; +mod class; +mod ex; +mod function; +mod globals; +mod handlers; +mod module; + +pub use _type::ZendType; +pub use class::ClassEntry; +pub use ex::ExecutionData; +pub use function::FunctionEntry; +pub use globals::ExecutorGlobals; +pub use handlers::ZendObjectHandlers; +pub use module::ModuleEntry; diff --git a/src/zend/module.rs b/src/zend/module.rs new file mode 100644 index 0000000000..f48b2d1897 --- /dev/null +++ b/src/zend/module.rs @@ -0,0 +1,13 @@ +//! Builder and objects for creating modules in PHP. A module is the base of a PHP extension. + +use crate::ffi::zend_module_entry; + +/// A Zend module entry. Alias. +pub type ModuleEntry = zend_module_entry; + +impl ModuleEntry { + /// Converts the module entry into a raw pointer, releasing it to the C world. + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} From 5232e2f305389132cd383afd3ed1cf04b8a9562f Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 7 Oct 2021 16:41:44 +1300 Subject: [PATCH 02/10] Fixed documentation tests --- example/skel/src/allocator.rs | 2 +- example/skel/src/lib.rs | 4 ++ ext-php-rs-derive/src/class.rs | 4 +- ext-php-rs-derive/src/constant.rs | 2 +- ext-php-rs-derive/src/extern_.rs | 2 +- ext-php-rs-derive/src/function.rs | 18 +++---- ext-php-rs-derive/src/method.rs | 22 ++++----- ext-php-rs-derive/src/module.rs | 18 +++---- ext-php-rs-derive/src/startup_function.rs | 8 +-- ext-php-rs-derive/src/zval.rs | 59 +++++++++++------------ guide/src/macros/classes.md | 6 +-- guide/src/types/binary.md | 5 +- guide/src/types/object.md | 10 ++-- src/builders/module.rs | 3 +- src/closure.rs | 4 +- src/constant.rs | 4 +- src/exception.rs | 8 +-- src/lib.rs | 10 ++-- src/macros.rs | 40 +++++++++------ src/props.rs | 14 +++--- src/types/array.rs | 3 ++ src/zend/handlers.rs | 2 +- 22 files changed, 132 insertions(+), 116 deletions(-) diff --git a/example/skel/src/allocator.rs b/example/skel/src/allocator.rs index d7f221d8dd..d9237d65a7 100644 --- a/example/skel/src/allocator.rs +++ b/example/skel/src/allocator.rs @@ -1,6 +1,6 @@ #![doc(hidden)] -use ext_php_rs::php::alloc; +use ext_php_rs::alloc; use std::alloc::GlobalAlloc; /// Global allocator which uses the Zend memory management APIs to allocate memory. diff --git a/example/skel/src/lib.rs b/example/skel/src/lib.rs index 127f5d983c..b4c6b1efe7 100644 --- a/example/skel/src/lib.rs +++ b/example/skel/src/lib.rs @@ -22,6 +22,10 @@ impl Default for TestClass { #[php_impl] impl TestClass { + fn __construct() -> Self { + Self::default() + } + #[getter] fn get_test_name(&self) -> String { self.c.clone() diff --git a/ext-php-rs-derive/src/class.rs b/ext-php-rs-derive/src/class.rs index 06af18ea7c..d5ea8d3551 100644 --- a/ext-php-rs-derive/src/class.rs +++ b/ext-php-rs-derive/src/class.rs @@ -199,7 +199,7 @@ impl Property { PropertyType::Field { field_name } => { let field_name = Ident::new(field_name, Span::call_site()); quote! { - (#name, ::ext_php_rs::php::types::props::Property::field(|obj: &mut Self| &mut obj.#field_name)), + (#name, ::ext_php_rs::props::Property::field(|obj: &mut Self| &mut obj.#field_name)), } } PropertyType::Method { getter, setter } => { @@ -216,7 +216,7 @@ impl Property { quote! { None } }; quote! { - (#name, ::ext_php_rs::php::types::props::Property::method(#getter, #setter)), + (#name, ::ext_php_rs::props::Property::method(#getter, #setter)), } } } diff --git a/ext-php-rs-derive/src/constant.rs b/ext-php-rs-derive/src/constant.rs index 09c2b44ddc..8418cf76e0 100644 --- a/ext-php-rs-derive/src/constant.rs +++ b/ext-php-rs-derive/src/constant.rs @@ -46,6 +46,6 @@ impl Constant { // Visibility::Private => quote! { Private }, // }; - // quote! { ::ext_php_rs::php::flags::ConstantFlags} + // quote! { ::ext_php_rs::flags::ConstantFlags} // } } diff --git a/ext-php-rs-derive/src/extern_.rs b/ext-php-rs-derive/src/extern_.rs index 8d47d09ec1..1db3109cd4 100644 --- a/ext-php-rs-derive/src/extern_.rs +++ b/ext-php-rs-derive/src/extern_.rs @@ -44,7 +44,7 @@ fn parse_function(mut func: ForeignItemFn) -> Result { #(#attrs)* #vis #sig { use ::std::convert::TryInto; - let callable = ::ext_php_rs::php::types::callable::Callable::try_from_name( + let callable = ::ext_php_rs::types::Callable::try_from_name( #name ).expect(concat!("Unable to find callable function `", #name, "`.")); diff --git a/ext-php-rs-derive/src/function.rs b/ext-php-rs-derive/src/function.rs index be66ac77bb..266dca0ac1 100644 --- a/ext-php-rs-derive/src/function.rs +++ b/ext-php-rs-derive/src/function.rs @@ -70,8 +70,8 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi #input #[doc(hidden)] - pub extern "C" fn #internal_ident(ex: &mut ::ext_php_rs::php::execution_data::ExecutionData, retval: &mut ::ext_php_rs::php::types::zval::Zval) { - use ::ext_php_rs::php::types::zval::IntoZval; + pub extern "C" fn #internal_ident(ex: &mut ::ext_php_rs::zend::ExecutionData, retval: &mut ::ext_php_rs::types::Zval) { + use ::ext_php_rs::convert::IntoZval; #(#arg_definitions)* #arg_parser @@ -79,7 +79,7 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi let result = #ident(#(#arg_accessors, )*); if let Err(e) = result.set_zval(retval, false) { - let e: ::ext_php_rs::php::exceptions::PhpException = e.into(); + let e: ::ext_php_rs::exception::PhpException = e.into(); e.throw().expect("Failed to throw exception"); } } @@ -206,7 +206,7 @@ pub fn build_arg_parser<'a>( let this = match this { Some(this) => this, None => { - ::ext_php_rs::php::exceptions::PhpException::default("Failed to retrieve reference to `$this`".into()) + ::ext_php_rs::exception::PhpException::default("Failed to retrieve reference to `$this`".into()) .throw() .unwrap(); return; @@ -309,7 +309,7 @@ impl Arg { pub fn get_type_ident(&self) -> TokenStream { let ty: Type = syn::parse_str(&self.ty).unwrap(); quote! { - <#ty as ::ext_php_rs::php::types::zval::FromZval>::TYPE + <#ty as ::ext_php_rs::convert::FromZval>::TYPE } } @@ -338,7 +338,7 @@ impl Arg { match #name_ident.val() { Some(val) => val, None => { - ::ext_php_rs::php::exceptions::PhpException::default( + ::ext_php_rs::exception::PhpException::default( concat!("Invalid value given for argument `", #name, "`.").into() ) .throw() @@ -363,7 +363,7 @@ impl Arg { }); quote! { - ::ext_php_rs::php::args::Arg::new(#name, #ty) #null #default + ::ext_php_rs::args::Arg::new(#name, #ty) #null #default } } } @@ -397,12 +397,12 @@ impl Function { // TODO allow reference returns? quote! { - .returns(<#ty as ::ext_php_rs::php::types::zval::IntoZval>::TYPE, false, #nullable) + .returns(<#ty as ::ext_php_rs::convert::IntoZval>::TYPE, false, #nullable) } }); quote! { - ::ext_php_rs::php::function::FunctionBuilder::new(#name, #name_ident) + ::ext_php_rs::builders::FunctionBuilder::new(#name, #name_ident) #(#args)* #output .build() diff --git a/ext-php-rs-derive/src/method.rs b/ext-php-rs-derive/src/method.rs index db122d5a9d..9b4dbb0963 100644 --- a/ext-php-rs-derive/src/method.rs +++ b/ext-php-rs-derive/src/method.rs @@ -154,10 +154,10 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result ::ext_php_rs::php::types::object::ConstructorResult { - use ::ext_php_rs::php::types::zval::IntoZval; - use ::ext_php_rs::php::types::object::ConstructorResult; + ex: &mut ::ext_php_rs::zend::ExecutionData + ) -> ::ext_php_rs::class::ConstructorResult { + use ::ext_php_rs::convert::IntoZval; + use ::ext_php_rs::class::ConstructorResult; #(#arg_definitions)* #arg_parser @@ -171,10 +171,10 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result Result::TYPE, false, #nullable) + .returns(<#ty as ::ext_php_rs::convert::IntoZval>::TYPE, false, #nullable) } }); quote! { - ::ext_php_rs::php::function::FunctionBuilder::new(#name, #class_path :: #name_ident) + ::ext_php_rs::builders::FunctionBuilder::new(#name, #class_path :: #name_ident) #(#args)* #output .build() @@ -338,7 +338,7 @@ impl Method { flags .iter() - .map(|flag| quote! { ::ext_php_rs::php::flags::MethodFlags::#flag }) + .map(|flag| quote! { ::ext_php_rs::flags::MethodFlags::#flag }) .collect::>() .to_token_stream() } diff --git a/ext-php-rs-derive/src/module.rs b/ext-php-rs-derive/src/module.rs index fcaf7645e7..e591bbb614 100644 --- a/ext-php-rs-derive/src/module.rs +++ b/ext-php-rs-derive/src/module.rs @@ -60,12 +60,12 @@ pub fn parser(input: ItemFn) -> Result { #[doc(hidden)] #[no_mangle] - pub extern "C" fn get_module() -> *mut ::ext_php_rs::php::module::ModuleEntry { + pub extern "C" fn get_module() -> *mut ::ext_php_rs::zend::ModuleEntry { fn internal(#inputs) #output { #(#stmts)* } - let mut builder = ::ext_php_rs::php::module::ModuleBuilder::new( + let mut builder = ::ext_php_rs::builders::ModuleBuilder::new( env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") ) @@ -98,10 +98,10 @@ pub fn generate_registered_class_impl(class: &Class) -> Result { let func = Ident::new(&constructor.ident, Span::call_site()); let args = constructor.get_arg_definitions(); quote! { - Some(::ext_php_rs::php::types::object::ConstructorMeta { + Some(::ext_php_rs::class::ConstructorMeta { constructor: Self::#func, build_fn: { - use ext_php_rs::php::function::FunctionBuilder; + use ::ext_php_rs::builders::FunctionBuilder; fn build_fn(func: FunctionBuilder) -> FunctionBuilder { func #(#args)* @@ -115,19 +115,19 @@ pub fn generate_registered_class_impl(class: &Class) -> Result { }; Ok(quote! { - static #meta: ::ext_php_rs::php::types::object::ClassMetadata<#self_ty> = ::ext_php_rs::php::types::object::ClassMetadata::new(); + static #meta: ::ext_php_rs::class::ClassMetadata<#self_ty> = ::ext_php_rs::class::ClassMetadata::new(); - impl ::ext_php_rs::php::types::object::RegisteredClass for #self_ty { + impl ::ext_php_rs::class::RegisteredClass for #self_ty { const CLASS_NAME: &'static str = #class_name; const CONSTRUCTOR: ::std::option::Option< - ::ext_php_rs::php::types::object::ConstructorMeta + ::ext_php_rs::class::ConstructorMeta > = #constructor; - fn get_metadata() -> &'static ::ext_php_rs::php::types::object::ClassMetadata { + fn get_metadata() -> &'static ::ext_php_rs::class::ClassMetadata { &#meta } - fn get_properties<'a>() -> ::std::collections::HashMap<&'static str, ::ext_php_rs::php::types::props::Property<'a, Self>> { + fn get_properties<'a>() -> ::std::collections::HashMap<&'static str, ::ext_php_rs::props::Property<'a, Self>> { use ::std::iter::FromIterator; ::std::collections::HashMap::from_iter([ diff --git a/ext-php-rs-derive/src/startup_function.rs b/ext-php-rs-derive/src/startup_function.rs index 0b9a7d6fd6..0a9a1354d6 100644 --- a/ext-php-rs-derive/src/startup_function.rs +++ b/ext-php-rs-derive/src/startup_function.rs @@ -21,14 +21,14 @@ pub fn parser(input: ItemFn) -> Result { let func = quote! { #[doc(hidden)] pub extern "C" fn #ident(ty: i32, module_number: i32) -> i32 { - use ::ext_php_rs::php::constants::IntoConst; - use ::ext_php_rs::php::flags::PropertyFlags; + use ::ext_php_rs::constant::IntoConst; + use ::ext_php_rs::flags::PropertyFlags; fn internal() { #(#stmts)* } - ::ext_php_rs::php::module::ext_php_rs_startup(); + ::ext_php_rs::internal::ext_php_rs_startup(); #(#classes)* #(#constants)* @@ -116,7 +116,7 @@ fn build_classes(classes: &HashMap) -> Result> { // .collect::>>()?; Ok(quote! {{ - let class = ::ext_php_rs::php::class::ClassBuilder::new(#class_name) + let class = ::ext_php_rs::builders::ClassBuilder::new(#class_name) #(#methods)* #(#constants)* #(#interfaces)* diff --git a/ext-php-rs-derive/src/zval.rs b/ext-php-rs-derive/src/zval.rs index dad0dde40b..b790ba3658 100644 --- a/ext-php-rs-derive/src/zval.rs +++ b/ext-php-rs-derive/src/zval.rs @@ -42,13 +42,13 @@ pub fn parser(input: DeriveInput) -> Result { let ident = &ty.ident; into_where_clause.predicates.push( syn::parse2(quote! { - #ident: ::ext_php_rs::php::types::zval::IntoZval + #ident: ::ext_php_rs::convert::IntoZval }) .expect("couldn't parse where predicate"), ); from_where_clause.predicates.push( syn::parse2(quote! { - #ident: ::ext_php_rs::php::types::zval::FromZval<'_zval> + #ident: ::ext_php_rs::convert::FromZval<'_zval> }) .expect("couldn't parse where predicate"), ); @@ -126,44 +126,43 @@ fn parse_struct( .collect::>>()?; Ok(quote! { - impl #into_impl_generics ::ext_php_rs::php::types::object::IntoZendObject for #ident #ty_generics #into_where_clause { - fn into_zend_object(self) -> ::ext_php_rs::errors::Result< - ::ext_php_rs::php::boxed::ZBox< - ::ext_php_rs::php::types::object::ZendObject + impl #into_impl_generics ::ext_php_rs::convert::IntoZendObject for #ident #ty_generics #into_where_clause { + fn into_zend_object(self) -> ::ext_php_rs::error::Result< + ::ext_php_rs::boxed::ZBox< + ::ext_php_rs::types::ZendObject > > { - use ::ext_php_rs::php::types::zval::IntoZval; + use ::ext_php_rs::convert::IntoZval; - let mut obj = ::ext_php_rs::php::types::object::ZendObject::new_stdclass(); + let mut obj = ::ext_php_rs::types::ZendObject::new_stdclass(); #(#into_fields)* - ::ext_php_rs::errors::Result::Ok(obj) + ::ext_php_rs::error::Result::Ok(obj) } } - impl #into_impl_generics ::ext_php_rs::php::types::zval::IntoZval for #ident #ty_generics #into_where_clause { - const TYPE: ::ext_php_rs::php::enums::DataType = ::ext_php_rs::php::enums::DataType::Object(None); + impl #into_impl_generics ::ext_php_rs::convert::IntoZval for #ident #ty_generics #into_where_clause { + const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Object(None); - fn set_zval(self, zv: &mut ::ext_php_rs::php::types::zval::Zval, persistent: bool) -> ::ext_php_rs::errors::Result<()> { - use ::ext_php_rs::php::types::zval::IntoZval; - use ::ext_php_rs::php::types::object::IntoZendObject; + fn set_zval(self, zv: &mut ::ext_php_rs::types::Zval, persistent: bool) -> ::ext_php_rs::error::Result<()> { + use ::ext_php_rs::convert::{IntoZval, IntoZendObject}; self.into_zend_object()?.set_zval(zv, persistent) } } - impl #from_impl_generics ::ext_php_rs::php::types::object::FromZendObject<'_zval> for #ident #ty_generics #from_where_clause { - fn from_zend_object(obj: &'_zval ::ext_php_rs::php::types::object::ZendObject) -> ::ext_php_rs::errors::Result { - ::ext_php_rs::errors::Result::Ok(Self { + impl #from_impl_generics ::ext_php_rs::convert::FromZendObject<'_zval> for #ident #ty_generics #from_where_clause { + fn from_zend_object(obj: &'_zval ::ext_php_rs::types::ZendObject) -> ::ext_php_rs::error::Result { + ::ext_php_rs::error::Result::Ok(Self { #(#from_fields)* }) } } - impl #from_impl_generics ::ext_php_rs::php::types::zval::FromZval<'_zval> for #ident #ty_generics #from_where_clause { - const TYPE: ::ext_php_rs::php::enums::DataType = ::ext_php_rs::php::enums::DataType::Object(None); + impl #from_impl_generics ::ext_php_rs::convert::FromZval<'_zval> for #ident #ty_generics #from_where_clause { + const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Object(None); - fn from_zval(zv: &'_zval ::ext_php_rs::php::types::zval::Zval) -> ::std::option::Option { - use ::ext_php_rs::php::types::object::FromZendObject; + fn from_zval(zv: &'_zval ::ext_php_rs::types::Zval) -> ::std::option::Option { + use ::ext_php_rs::convert::FromZendObject; Self::from_zend_object(zv.object()?).ok() } @@ -230,30 +229,30 @@ fn parse_enum( let default = default.unwrap_or_else(|| quote! { None }); Ok(quote! { - impl #into_impl_generics ::ext_php_rs::php::types::zval::IntoZval for #ident #ty_generics #into_where_clause { - const TYPE: ::ext_php_rs::php::enums::DataType = ::ext_php_rs::php::enums::DataType::Mixed; + impl #into_impl_generics ::ext_php_rs::convert::IntoZval for #ident #ty_generics #into_where_clause { + const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Mixed; fn set_zval( self, - zv: &mut ::ext_php_rs::php::types::zval::Zval, + zv: &mut ::ext_php_rs::types::Zval, persistent: bool, - ) -> ::ext_php_rs::errors::Result<()> { - use ::ext_php_rs::php::types::zval::IntoZval; + ) -> ::ext_php_rs::error::Result<()> { + use ::ext_php_rs::convert::IntoZval; match self { #(#into_variants,)* _ => { zv.set_null(); - ::ext_php_rs::errors::Result::Ok(()) + ::ext_php_rs::error::Result::Ok(()) } } } } - impl #from_impl_generics ::ext_php_rs::php::types::zval::FromZval<'_zval> for #ident #ty_generics #from_where_clause { - const TYPE: ::ext_php_rs::php::enums::DataType = ::ext_php_rs::php::enums::DataType::Mixed; + impl #from_impl_generics ::ext_php_rs::convert::FromZval<'_zval> for #ident #ty_generics #from_where_clause { + const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Mixed; - fn from_zval(zval: &'_zval ::ext_php_rs::php::types::zval::Zval) -> ::std::option::Option { + fn from_zval(zval: &'_zval ::ext_php_rs::types::Zval) -> ::std::option::Option { #(#from_variants)* #default } diff --git a/guide/src/macros/classes.md b/guide/src/macros/classes.md index 8ff62736dd..e44246ae8e 100644 --- a/guide/src/macros/classes.md +++ b/guide/src/macros/classes.md @@ -57,11 +57,11 @@ it in the `Redis\Exception` namespace: ```rust # extern crate ext_php_rs; -# use ext_php_rs::prelude::*; -use ext_php_rs::php::{class::ClassEntry, exceptions::PhpException}; +use ext_php_rs::prelude::*; +use ext_php_rs::{exception::PhpException, zend::ce}; #[php_class(name = "Redis\\Exception\\RedisException")] -#[extends(ClassEntry::exception())] +#[extends(ce::exception())] #[derive(Default)] pub struct RedisException; diff --git a/guide/src/types/binary.md b/guide/src/types/binary.md index 82917d4255..f342ccdc0a 100644 --- a/guide/src/types/binary.md +++ b/guide/src/types/binary.md @@ -23,8 +23,9 @@ f32, f64). ```rust # extern crate ext_php_rs; -# use ext_php_rs::prelude::*; -# use ext_php_rs::php::types::binary::Binary; +use ext_php_rs::prelude::*; +use ext_php_rs::binary::Binary; + #[php_function] pub fn test_binary(input: Binary) -> Binary { for i in input.iter() { diff --git a/guide/src/types/object.md b/guide/src/types/object.md index baf8ce78c7..aaec16dab8 100644 --- a/guide/src/types/object.md +++ b/guide/src/types/object.md @@ -4,13 +4,13 @@ Objects can be returned from functions as instances or references. You can only return a reference when you are returning an immutable reference to the object the method is implemented on. -| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | -| ------------- | -------------- | --------------- | --------------------- | -------------------------------- | -| No | No | Yes | Yes, as `ClassRef` | A Rust struct and a Zend object. | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | -------------------------------- | +| No | No | Yes | No - planned | A Rust struct and a Zend object. | ## Examples -### Returning a reference to `self` + ### Creating a new class instance diff --git a/src/builders/module.rs b/src/builders/module.rs index 54e7c0b393..49b01f6b41 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -14,7 +14,8 @@ use std::{ /// /// ``` /// use ext_php_rs::{ -/// php::module::{ModuleEntry, ModuleBuilder}, +/// builders::ModuleBuilder, +/// zend::ModuleEntry, /// info_table_start, info_table_end, info_table_row /// }; /// diff --git a/src/closure.rs b/src/closure.rs index 49120a3c4f..41dd829ce4 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -62,7 +62,7 @@ impl Closure { /// # Example /// /// ```rust,no_run - /// use ext_php_rs::php::types::closure::Closure; + /// use ext_php_rs::closure::Closure; /// /// let closure = Closure::wrap(Box::new(|name| { /// format!("Hello {}", name) @@ -89,7 +89,7 @@ impl Closure { /// # Example /// /// ```rust,no_run - /// use ext_php_rs::php::types::closure::Closure; + /// use ext_php_rs::closure::Closure; /// /// let name: String = "Hello world".into(); /// let closure = Closure::wrap_once(Box::new(|| { diff --git a/src/constant.rs b/src/constant.rs index 6d7a3577b2..bc33f0f081 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -26,7 +26,7 @@ pub trait IntoConst: Sized { /// # Examples /// /// ```no_run - /// use ext_php_rs::php::{constants::IntoConst, flags::ZendResult}; + /// use ext_php_rs::constant::IntoConst; /// /// pub extern "C" fn startup_function(_type: i32, module_number: i32) -> i32 { /// 5.register_constant("MY_CONST_NAME", module_number); // MY_CONST_NAME == 5 @@ -60,7 +60,7 @@ pub trait IntoConst: Sized { /// # Examples /// /// ```no_run - /// use ext_php_rs::php::{constants::IntoConst, flags::{GlobalConstantFlags, ZendResult}}; + /// use ext_php_rs::{constant::IntoConst, flags::GlobalConstantFlags}; /// /// pub extern "C" fn startup_function(_type: i32, module_number: i32) -> i32 { /// 42.register_constant_flags("MY_CONST_NAME", module_number, GlobalConstantFlags::Persistent | GlobalConstantFlags::Deprecated); diff --git a/src/exception.rs b/src/exception.rs index 9265e88906..cc9a23b974 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -90,9 +90,9 @@ impl From<&str> for PhpException { /// # Examples /// /// ```no_run -/// use ext_php_rs::php::{class::ClassEntry, exceptions::throw}; +/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw}; /// -/// throw(ClassEntry::compile_error(), "This is a CompileError."); +/// throw(ce::compile_error(), "This is a CompileError."); /// ``` pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> { throw_with_code(ex, 0, message) @@ -112,9 +112,9 @@ pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> { /// # Examples /// /// ```no_run -/// use ext_php_rs::php::{class::ClassEntry, exceptions::throw_with_code}; +/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw_with_code}; /// -/// throw_with_code(ClassEntry::compile_error(), 123, "This is a CompileError."); +/// throw_with_code(ce::compile_error(), 123, "This is a CompileError."); /// ``` pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> { let flags = ex.flags(); diff --git a/src/lib.rs b/src/lib.rs index e9f58232b6..8e9f9fefc8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ pub use ext_php_rs_derive::php_const; /// /// ``` /// # use ext_php_rs::prelude::*; -/// # use ext_php_rs::php::types::zval::Zval; +/// # use ext_php_rs::types::Zval; /// #[php_extern] /// extern "C" { /// fn strpos(haystack: &str, needle: &str, offset: Option) -> Zval; @@ -156,7 +156,7 @@ pub use ext_php_rs_derive::php_extern; /// PHP. The first example function would be converted into a function which looks like so: /// /// ```no_run -/// # use ext_php_rs::{prelude::*, php::{exceptions::PhpException, execution_data::ExecutionData, types::zval::{FromZval, IntoZval, Zval}, args::{Arg, ArgParser}}}; +/// # use ext_php_rs::{prelude::*, exception::PhpException, zend::ExecutionData, convert::{FromZval, IntoZval}, types::Zval, args::{Arg, ArgParser}}; /// pub fn hello(name: String) -> String { /// format!("Hello, {}!", name) /// } @@ -431,11 +431,11 @@ pub use ext_php_rs_derive::php_module; /// /// ``` /// # use ext_php_rs::prelude::*; -/// use ext_php_rs::php::exceptions::PhpException; -/// use ext_php_rs::php::class::ClassEntry; +/// use ext_php_rs::exception::PhpException; +/// use ext_php_rs::zend::ce; /// /// #[php_class(name = "Redis\\Exception\\RedisException")] -/// #[extends(ClassEntry::exception())] +/// #[extends(ce::exception())] /// pub struct Example; /// /// #[php_function] diff --git a/src/macros.rs b/src/macros.rs index 118d14cf3c..e693e9c798 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -7,7 +7,7 @@ #[macro_export] macro_rules! info_table_start { () => { - unsafe { $crate::bindings::php_info_print_table_start() }; + unsafe { $crate::ffi::php_info_print_table_start() }; }; } @@ -15,7 +15,7 @@ macro_rules! info_table_start { #[macro_export] macro_rules! info_table_end { () => { - unsafe { $crate::bindings::php_info_print_table_end() } + unsafe { $crate::ffi::php_info_print_table_end() } }; } @@ -37,7 +37,7 @@ macro_rules! info_table_row { macro_rules! _info_table_row { ($fn: ident, $($element: expr),*) => { unsafe { - $crate::bindings::$fn($crate::_info_table_row!(@COUNT; $($element),*) as i32, $(::std::ffi::CString::new($element).unwrap().as_ptr()),*); + $crate::ffi::$fn($crate::_info_table_row!(@COUNT; $($element),*) as i32, $(::std::ffi::CString::new($element).unwrap().as_ptr()),*); } }; @@ -79,8 +79,11 @@ macro_rules! call_user_func { /// ``` /// # #[macro_use] extern crate ext_php_rs; /// use ext_php_rs::{ -/// parse_args, -/// php::{args::Arg, enums::DataType, execution_data::ExecutionData, types::zval::Zval}, +/// parse_args, +/// args::Arg, +/// flags::DataType, +/// zend::ExecutionData, +/// types::Zval, /// }; /// /// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { @@ -97,8 +100,11 @@ macro_rules! call_user_func { /// /// ``` /// use ext_php_rs::{ -/// parse_args, -/// php::{args::Arg, enums::DataType, execution_data::ExecutionData, types::zval::Zval}, +/// parse_args, +/// args::Arg, +/// flags::DataType, +/// zend::ExecutionData, +/// types::Zval, /// }; /// /// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { @@ -121,8 +127,6 @@ macro_rules! parse_args { }}; ($ed: expr, $($arg: expr),* ; $($opt: expr),*) => {{ - use $crate::php::args::ArgParser; - let parser = $ed.parser() $(.arg(&mut $arg))* .not_required() @@ -143,12 +147,16 @@ macro_rules! parse_args { /// # Examples /// /// ``` -/// use ext_php_rs::{throw, php::{class::ClassEntry, execution_data::ExecutionData, types::zval::Zval}}; +/// use ext_php_rs::{ +/// throw, +/// zend::{ce, ClassEntry, ExecutionData}, +/// types::Zval, +/// }; /// /// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { /// let something_wrong = true; /// if something_wrong { -/// throw!(ClassEntry::exception(), "Something is wrong!"); +/// throw!(ce::exception(), "Something is wrong!"); /// } /// /// assert!(false); // This will not run. @@ -157,7 +165,7 @@ macro_rules! parse_args { #[macro_export] macro_rules! throw { ($ex: expr, $reason: expr) => { - $crate::php::exceptions::throw($ex, $reason); + $crate::exception::throw($ex, $reason); return; }; } @@ -180,7 +188,7 @@ macro_rules! throw { /// # Examples /// /// ``` -/// # use ext_php_rs::{php::types::{zval::{Zval, IntoZval, FromZval}, object::{ZendObject, RegisteredClass}}}; +/// # use ext_php_rs::{convert::{IntoZval, FromZval}, types::{Zval, ZendObject}, class::{RegisteredClass}}; /// use ext_php_rs::class_derives; /// /// struct Test { @@ -191,14 +199,14 @@ macro_rules! throw { /// impl RegisteredClass for Test { /// const CLASS_NAME: &'static str = "Test"; /// -/// const CONSTRUCTOR: Option> = None; +/// const CONSTRUCTOR: Option> = None; /// -/// fn get_metadata() -> &'static ext_php_rs::php::types::object::ClassMetadata { +/// fn get_metadata() -> &'static ext_php_rs::class::ClassMetadata { /// todo!() /// } /// /// fn get_properties<'a>( -/// ) -> std::collections::HashMap<&'static str, ext_php_rs::php::types::props::Property<'a, Self>> +/// ) -> std::collections::HashMap<&'static str, ext_php_rs::props::Property<'a, Self>> /// { /// todo!() /// } diff --git a/src/props.rs b/src/props.rs index 0a2813c621..c30d03d61e 100644 --- a/src/props.rs +++ b/src/props.rs @@ -65,7 +65,7 @@ impl<'a, T: 'a> Property<'a, T> { /// # Examples /// /// ```no_run - /// # use ext_php_rs::php::types::props::Property; + /// # use ext_php_rs::props::Property; /// struct Test { /// pub a: i32, /// } @@ -92,7 +92,7 @@ impl<'a, T: 'a> Property<'a, T> { /// # Examples /// /// ```no_run - /// # use ext_php_rs::php::types::props::Property; + /// # use ext_php_rs::props::Property; /// struct Test; /// /// impl Test { @@ -150,8 +150,8 @@ impl<'a, T: 'a> Property<'a, T> { /// # Examples /// /// ```no_run - /// # use ext_php_rs::php::types::props::Property; - /// # use ext_php_rs::php::types::zval::Zval; + /// # use ext_php_rs::props::Property; + /// # use ext_php_rs::types::Zval; /// struct Test { /// pub a: i32, /// } @@ -194,9 +194,9 @@ impl<'a, T: 'a> Property<'a, T> { /// # Examples /// /// ```no_run - /// # use ext_php_rs::php::types::props::Property; - /// # use ext_php_rs::php::types::zval::Zval; - /// # use ext_php_rs::php::types::zval::IntoZval; + /// # use ext_php_rs::props::Property; + /// # use ext_php_rs::types::Zval; + /// # use ext_php_rs::convert::IntoZval; /// struct Test { /// pub a: i32, /// } diff --git a/src/types/array.rs b/src/types/array.rs index 5ce9cc44b8..7271cdafd7 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -28,6 +28,9 @@ use crate::{ /// PHP array, which is represented in memory as a hashtable. pub type HashTable = crate::ffi::HashTable; +// Clippy complains about there being no `is_empty` function when implementing on the alias `ZendStr` :( +// +#[allow(clippy::len_without_is_empty)] impl HashTable { /// Creates a new, empty, PHP associative array. pub fn new() -> ZBox { diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 3ff3fd62b3..6d600985a7 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -43,7 +43,7 @@ impl ZendObjectHandlers { .expect("Invalid object pointer given for `free_obj`"); // Manually drop the object as we don't want to free the underlying memory. - ptr::drop_in_place(&mut **obj); + ptr::drop_in_place(&mut obj.obj); zend_object_std_dtor(object) } From b8488492d49ce3c3d77feb4a5f56d9d4f82be6ab Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 7 Oct 2021 16:44:22 +1300 Subject: [PATCH 03/10] Removed skel, moved macro crate --- Cargo.toml | 5 +- README.md | 2 - crates/macros/.gitignore | 1 + .../macros}/Cargo.toml | 0 crates/macros/LICENSE_APACHE | 1 + crates/macros/LICENSE_MIT | 1 + crates/macros/README.md | 1 + .../macros}/src/class.rs | 0 .../macros}/src/constant.rs | 0 .../macros}/src/extern_.rs | 0 .../macros}/src/function.rs | 0 .../macros}/src/impl_.rs | 0 .../macros}/src/lib.rs | 0 .../macros}/src/method.rs | 0 .../macros}/src/module.rs | 0 .../macros}/src/startup_function.rs | 0 .../macros}/src/syn_ext.rs | 0 .../macros}/src/zval.rs | 0 example/skel/.gitignore | 6 -- example/skel/Cargo.toml | 14 ---- example/skel/composer.json | 13 ---- example/skel/php | 9 --- example/skel/src/allocator.rs | 31 --------- example/skel/src/lib.rs | 65 ------------------- example/skel/test.php | 2 - ext-php-rs-derive/.gitignore | 1 - ext-php-rs-derive/LICENSE_APACHE | 1 - ext-php-rs-derive/LICENSE_MIT | 1 - ext-php-rs-derive/README.md | 1 - 29 files changed, 6 insertions(+), 149 deletions(-) create mode 120000 crates/macros/.gitignore rename {ext-php-rs-derive => crates/macros}/Cargo.toml (100%) create mode 120000 crates/macros/LICENSE_APACHE create mode 120000 crates/macros/LICENSE_MIT create mode 120000 crates/macros/README.md rename {ext-php-rs-derive => crates/macros}/src/class.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/constant.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/extern_.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/function.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/impl_.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/lib.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/method.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/module.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/startup_function.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/syn_ext.rs (100%) rename {ext-php-rs-derive => crates/macros}/src/zval.rs (100%) delete mode 100644 example/skel/.gitignore delete mode 100644 example/skel/Cargo.toml delete mode 100644 example/skel/composer.json delete mode 100755 example/skel/php delete mode 100644 example/skel/src/allocator.rs delete mode 100644 example/skel/src/lib.rs delete mode 100644 example/skel/test.php delete mode 120000 ext-php-rs-derive/.gitignore delete mode 120000 ext-php-rs-derive/LICENSE_APACHE delete mode 120000 ext-php-rs-derive/LICENSE_MIT delete mode 120000 ext-php-rs-derive/README.md diff --git a/Cargo.toml b/Cargo.toml index dde1a4beca..773fb1ab64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = ["api-bindings"] [dependencies] bitflags = "1.2.1" parking_lot = "0.11.2" -ext-php-rs-derive = { version = "=0.5.2", path = "./ext-php-rs-derive" } +ext-php-rs-derive = { version = "=0.5.2", path = "./crates/macros" } [build-dependencies] bindgen = { version = "0.59" } @@ -25,8 +25,7 @@ closure = [] [workspace] members = [ - "ext-php-rs-derive", - "example/skel" + "crates/macros", ] [package.metadata.docs.rs] diff --git a/README.md b/README.md index ae198ffe46..c9b4720def 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,6 @@ easily), structs have to be hard coded in. Check out one of the example projects: -- [ext-skel](example/skel) - Testbed for testing the library. Check out previous - commits as well to see what else is possible. - [anonaddy-sequoia](https://gitlab.com/willbrowning/anonaddy-sequoia) - Sequoia encryption PHP extension. - [opus-php](https://github.com/davidcole1340/opus-php/tree/rewrite_rs) - diff --git a/crates/macros/.gitignore b/crates/macros/.gitignore new file mode 120000 index 0000000000..6ef08f9d15 --- /dev/null +++ b/crates/macros/.gitignore @@ -0,0 +1 @@ +../../.gitignore \ No newline at end of file diff --git a/ext-php-rs-derive/Cargo.toml b/crates/macros/Cargo.toml similarity index 100% rename from ext-php-rs-derive/Cargo.toml rename to crates/macros/Cargo.toml diff --git a/crates/macros/LICENSE_APACHE b/crates/macros/LICENSE_APACHE new file mode 120000 index 0000000000..bd7902319b --- /dev/null +++ b/crates/macros/LICENSE_APACHE @@ -0,0 +1 @@ +../../LICENSE_APACHE \ No newline at end of file diff --git a/crates/macros/LICENSE_MIT b/crates/macros/LICENSE_MIT new file mode 120000 index 0000000000..0304881f49 --- /dev/null +++ b/crates/macros/LICENSE_MIT @@ -0,0 +1 @@ +../../LICENSE_MIT \ No newline at end of file diff --git a/crates/macros/README.md b/crates/macros/README.md new file mode 120000 index 0000000000..fe84005413 --- /dev/null +++ b/crates/macros/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/ext-php-rs-derive/src/class.rs b/crates/macros/src/class.rs similarity index 100% rename from ext-php-rs-derive/src/class.rs rename to crates/macros/src/class.rs diff --git a/ext-php-rs-derive/src/constant.rs b/crates/macros/src/constant.rs similarity index 100% rename from ext-php-rs-derive/src/constant.rs rename to crates/macros/src/constant.rs diff --git a/ext-php-rs-derive/src/extern_.rs b/crates/macros/src/extern_.rs similarity index 100% rename from ext-php-rs-derive/src/extern_.rs rename to crates/macros/src/extern_.rs diff --git a/ext-php-rs-derive/src/function.rs b/crates/macros/src/function.rs similarity index 100% rename from ext-php-rs-derive/src/function.rs rename to crates/macros/src/function.rs diff --git a/ext-php-rs-derive/src/impl_.rs b/crates/macros/src/impl_.rs similarity index 100% rename from ext-php-rs-derive/src/impl_.rs rename to crates/macros/src/impl_.rs diff --git a/ext-php-rs-derive/src/lib.rs b/crates/macros/src/lib.rs similarity index 100% rename from ext-php-rs-derive/src/lib.rs rename to crates/macros/src/lib.rs diff --git a/ext-php-rs-derive/src/method.rs b/crates/macros/src/method.rs similarity index 100% rename from ext-php-rs-derive/src/method.rs rename to crates/macros/src/method.rs diff --git a/ext-php-rs-derive/src/module.rs b/crates/macros/src/module.rs similarity index 100% rename from ext-php-rs-derive/src/module.rs rename to crates/macros/src/module.rs diff --git a/ext-php-rs-derive/src/startup_function.rs b/crates/macros/src/startup_function.rs similarity index 100% rename from ext-php-rs-derive/src/startup_function.rs rename to crates/macros/src/startup_function.rs diff --git a/ext-php-rs-derive/src/syn_ext.rs b/crates/macros/src/syn_ext.rs similarity index 100% rename from ext-php-rs-derive/src/syn_ext.rs rename to crates/macros/src/syn_ext.rs diff --git a/ext-php-rs-derive/src/zval.rs b/crates/macros/src/zval.rs similarity index 100% rename from ext-php-rs-derive/src/zval.rs rename to crates/macros/src/zval.rs diff --git a/example/skel/.gitignore b/example/skel/.gitignore deleted file mode 100644 index 43db77f061..0000000000 --- a/example/skel/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/target -Cargo.lock -/.vscode -expand.rs -vendor -composer.lock diff --git a/example/skel/Cargo.toml b/example/skel/Cargo.toml deleted file mode 100644 index 426e778e52..0000000000 --- a/example/skel/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "skel" -version = "0.1.0" -authors = ["David Cole "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ext-php-rs = { path = "../../", features = ["closure"] } - -[lib] -name = "skel" -crate-type = ["cdylib"] diff --git a/example/skel/composer.json b/example/skel/composer.json deleted file mode 100644 index fb8425595f..0000000000 --- a/example/skel/composer.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "rust/skel", - "license": "MIT OR Apache-2.0", - "authors": [ - { - "name": "David Cole", - "email": "david.cole1340@gmail.com" - } - ], - "require": { - "psy/psysh": "^0.10.8" - } -} diff --git a/example/skel/php b/example/skel/php deleted file mode 100755 index bdc549bdfa..0000000000 --- a/example/skel/php +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -case "$(uname)" in - Darwin) EXT="dylib" ;; - Linux) EXT="so" ;; - *) EXT="so" ;; -esac - -cargo build && php -dextension=$PWD/../../target/debug/libskel.$EXT "${@}" diff --git a/example/skel/src/allocator.rs b/example/skel/src/allocator.rs deleted file mode 100644 index d9237d65a7..0000000000 --- a/example/skel/src/allocator.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![doc(hidden)] - -use ext_php_rs::alloc; -use std::alloc::GlobalAlloc; - -/// Global allocator which uses the Zend memory management APIs to allocate memory. -/// -/// At the moment, this should only be used for debugging memory leaks. You are not supposed to -/// allocate non-request-bound memory using the Zend memory management API. -#[derive(Default)] -pub struct PhpAllocator {} - -impl PhpAllocator { - /// Creates a new PHP allocator. - pub const fn new() -> Self { - Self {} - } -} - -unsafe impl GlobalAlloc for PhpAllocator { - unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 { - let ptr = alloc::emalloc(layout); - eprintln!("allocating {:?}: {} bytes", ptr, layout.size()); - ptr - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) { - eprintln!("freeing {:?}: {} bytes", ptr, layout.size()); - alloc::efree(ptr) - } -} diff --git a/example/skel/src/lib.rs b/example/skel/src/lib.rs deleted file mode 100644 index b4c6b1efe7..0000000000 --- a/example/skel/src/lib.rs +++ /dev/null @@ -1,65 +0,0 @@ -use ext_php_rs::prelude::*; - -#[php_class] -struct TestClass { - #[prop(rename = "Hello")] - a: i32, - #[prop] - b: i64, - #[prop] - c: String, -} - -impl Default for TestClass { - fn default() -> Self { - Self { - a: 100, - b: 123, - c: "Hello, world!".into(), - } - } -} - -#[php_impl] -impl TestClass { - fn __construct() -> Self { - Self::default() - } - - #[getter] - fn get_test_name(&self) -> String { - self.c.clone() - } - - #[setter] - fn set_test_name(&mut self, c: String) { - self.c = c; - } -} - -#[derive(Debug, ZvalConvert)] -pub struct TestStdClass -where - A: PartialEq, -{ - a: A, - b: B, - c: C, -} - -#[derive(Debug, ZvalConvert)] -pub enum UnionExample<'a, T> { - B(T), - C(&'a str), - None, -} - -#[php_function] -pub fn test_union(union: UnionExample) { - dbg!(union); -} - -#[php_module] -pub fn module(module: ModuleBuilder) -> ModuleBuilder { - module -} diff --git a/example/skel/test.php b/example/skel/test.php deleted file mode 100644 index a4abe2dafc..0000000000 --- a/example/skel/test.php +++ /dev/null @@ -1,2 +0,0 @@ - Date: Thu, 7 Oct 2021 16:46:52 +1300 Subject: [PATCH 04/10] Ignore folders for crate publish --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 773fb1ab64..cf85e94e3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ version = "0.5.3" authors = ["David Cole "] edition = "2018" categories = ["api-bindings"] +exclude = ["/.github", "/.crates", "/guide"] [dependencies] bitflags = "1.2.1" From a192fbb2e1fd2761afaa802ceab1af72effc200e Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 7 Oct 2021 17:08:23 +1300 Subject: [PATCH 05/10] Fix builder for zts --- src/builders/module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builders/module.rs b/src/builders/module.rs index 49b01f6b41..18a0635c6b 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -73,7 +73,7 @@ impl ModuleBuilder { #[cfg(not(php_zts))] globals_ptr: ptr::null::() as *mut c_void, #[cfg(php_zts)] - globals_id_ptr: ptr::null::() as *mut crate::bindings::ts_rsrc_id, + globals_id_ptr: ptr::null::() as *mut crate::ffi::ts_rsrc_id, globals_ctor: None, globals_dtor: None, post_deactivate_func: None, From 987eddd4c8c74ea6686bfef33205123ba9b2756a Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 7 Oct 2021 17:14:03 +1300 Subject: [PATCH 06/10] Add `rustfmt.toml`, wrap all comments #96 --- build.rs | 9 +- crates/macros/src/function.rs | 6 +- crates/macros/src/zval.rs | 5 +- rustfmt.toml | 1 + src/alloc.rs | 10 +- src/args.rs | 36 ++-- src/binary.rs | 46 +++--- src/boxed.rs | 62 ++++--- src/builders/class.rs | 42 +++-- src/builders/function.rs | 6 +- src/builders/module.rs | 10 +- src/class.rs | 55 ++++--- src/closure.rs | 72 ++++---- src/constant.rs | 37 +++-- src/convert.rs | 60 ++++--- src/error.rs | 13 +- src/exception.rs | 33 ++-- src/internal.rs | 4 +- src/lib.rs | 301 +++++++++++++++++++--------------- src/macros.rs | 53 +++--- src/props.rs | 42 +++-- src/types/array.rs | 47 +++--- src/types/callable.rs | 29 ++-- src/types/class_object.rs | 71 ++++---- src/types/long.rs | 5 +- src/types/object.rs | 54 +++--- src/types/string.rs | 81 +++++---- src/types/zval.rs | 93 ++++++----- src/zend/_type.rs | 25 ++- src/zend/class.rs | 18 +- src/zend/ex.rs | 53 +++--- src/zend/function.rs | 3 +- src/zend/globals.rs | 3 +- src/zend/handlers.rs | 8 +- src/zend/module.rs | 6 +- 35 files changed, 811 insertions(+), 588 deletions(-) create mode 100644 rustfmt.toml diff --git a/build.rs b/build.rs index ef84ec316c..110268d072 100644 --- a/build.rs +++ b/build.rs @@ -40,8 +40,8 @@ fn main() { } // Ensure the PHP API version is supported. - // We could easily use grep and sed here but eventually we want to support Windows, - // so it's easier to just use regex. + // We could easily use grep and sed here but eventually we want to support + // Windows, so it's easier to just use regex. let php_i_cmd = Command::new("php") .arg("-i") .output() @@ -143,8 +143,9 @@ impl Configure { } } -/// Array of functions/types used in `ext-php-rs` - used to allowlist when generating -/// bindings, as we don't want to generate bindings for everything (i.e. stdlib headers). +/// Array of functions/types used in `ext-php-rs` - used to allowlist when +/// generating bindings, as we don't want to generate bindings for everything +/// (i.e. stdlib headers). const ALLOWED_BINDINGS: &[&str] = &[ "HashTable", "_Bucket", diff --git a/crates/macros/src/function.rs b/crates/macros/src/function.rs index 266dca0ac1..d5901edb2c 100644 --- a/crates/macros/src/function.rs +++ b/crates/macros/src/function.rs @@ -318,7 +318,8 @@ impl Arg { Ident::new(&self.name, Span::call_site()) } - /// Returns a [`TokenStream`] containing the line required to retrieve the value from the argument. + /// Returns a [`TokenStream`] containing the line required to retrieve the + /// value from the argument. pub fn get_accessor(&self, ret: &TokenStream) -> TokenStream { let name = &self.name; let name_ident = self.get_name_ident(); @@ -350,7 +351,8 @@ impl Arg { } } - /// Returns a [`TokenStream`] containing the line required to instantiate the argument. + /// Returns a [`TokenStream`] containing the line required to instantiate + /// the argument. pub fn get_arg_definition(&self) -> TokenStream { let name = &self.name; let ty = self.get_type_ident(); diff --git a/crates/macros/src/zval.rs b/crates/macros/src/zval.rs index b790ba3658..ca60bb2d80 100644 --- a/crates/macros/src/zval.rs +++ b/crates/macros/src/zval.rs @@ -22,8 +22,9 @@ pub fn parser(input: DeriveInput) -> Result { }); let mut from_where_clause = into_where_clause.clone(); - // FIXME(david): work around since mutating `generics` will add the lifetime to the struct generics as well, - // leading to an error as we would have `impl<'a> FromZendObject<'a> for Struct<'a>` when `Struct` has no lifetime. + // FIXME(david): work around since mutating `generics` will add the lifetime to + // the struct generics as well, leading to an error as we would have + // `impl<'a> FromZendObject<'a> for Struct<'a>` when `Struct` has no lifetime. let from_impl_generics = { let tokens = into_impl_generics.to_token_stream(); let mut parsed: Generics = syn::parse2(tokens).expect("couldn't reparse generics"); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..606e29234d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +wrap_comments = true \ No newline at end of file diff --git a/src/alloc.rs b/src/alloc.rs index f425f82def..3bad5ee954 100644 --- a/src/alloc.rs +++ b/src/alloc.rs @@ -1,4 +1,5 @@ -//! Functions relating to the Zend Memory Manager, used to allocate request-bound memory. +//! Functions relating to the Zend Memory Manager, used to allocate +//! request-bound memory. use crate::ffi::{_efree, _emalloc}; use std::{alloc::Layout, ffi::c_void}; @@ -28,7 +29,8 @@ pub fn emalloc(layout: Layout) -> *mut u8 { }) as *mut u8 } -/// Frees a given memory pointer which was allocated through the PHP memory manager. +/// Frees a given memory pointer which was allocated through the PHP memory +/// manager. /// /// # Parameters /// @@ -36,8 +38,8 @@ pub fn emalloc(layout: Layout) -> *mut u8 { /// /// # Safety /// -/// Caller must guarantee that the given pointer is valid (aligned and non-null) and -/// was originally allocated through the Zend memory manager. +/// Caller must guarantee that the given pointer is valid (aligned and non-null) +/// and was originally allocated through the Zend memory manager. pub unsafe fn efree(ptr: *mut u8) { #[cfg(php_debug)] { diff --git a/src/args.rs b/src/args.rs index 4920ff32ae..e73cadf056 100644 --- a/src/args.rs +++ b/src/args.rs @@ -73,13 +73,14 @@ impl<'a> Arg<'a> { self } - /// Attempts to consume the argument, converting the inner type into `T`. Upon success, - /// the result is returned in a [`Result`]. + /// Attempts to consume the argument, converting the inner type into `T`. + /// Upon success, the result is returned in a [`Result`]. /// - /// If the conversion fails (or the argument contains no value), the argument is returned - /// in an [`Err`] variant. + /// If the conversion fails (or the argument contains no value), the + /// argument is returned in an [`Err`] variant. /// - /// As this function consumes, it cannot return a reference to the underlying zval. + /// As this function consumes, it cannot return a reference to the + /// underlying zval. pub fn consume(mut self) -> Result where for<'b> T: FromZvalMut<'b>, @@ -110,12 +111,14 @@ impl<'a> Arg<'a> { self.zval.as_mut() } - /// Attempts to call the argument as a callable with a list of arguments to pass to the function. - /// Note that a thrown exception inside the callable is not detectable, therefore you should - /// check if the return value is valid rather than unwrapping. Returns a result containing the + /// Attempts to call the argument as a callable with a list of arguments to + /// pass to the function. Note that a thrown exception inside the + /// callable is not detectable, therefore you should check if the return + /// value is valid rather than unwrapping. Returns a result containing the /// return value of the function, or an error. /// - /// You should not call this function directly, rather through the [`call_user_func`] macro. + /// You should not call this function directly, rather through the + /// [`call_user_func`] macro. /// /// # Parameters /// @@ -201,9 +204,11 @@ impl<'a, 'b> ArgParser<'a, 'b> { } /// Uses the argument parser to parse the arguments contained in the given - /// `ExecutionData` object. Returns successfully if the arguments were parsed. + /// `ExecutionData` object. Returns successfully if the arguments were + /// parsed. /// - /// This function can only be safely called from within an exported PHP function. + /// This function can only be safely called from within an exported PHP + /// function. /// /// # Parameters /// @@ -211,16 +216,17 @@ impl<'a, 'b> ArgParser<'a, 'b> { /// /// # Errors /// - /// Returns an [`Error`] type if there were too many or too little arguments passed to the - /// function. The user has already been notified so you should break execution after seeing an - /// error type. + /// Returns an [`Error`] type if there were too many or too little arguments + /// passed to the function. The user has already been notified so you + /// should break execution after seeing an error type. pub fn parse(mut self) -> Result<()> { let max_num_args = self.args.len(); let min_num_args = self.min_num_args.unwrap_or(max_num_args); let num_args = self.arg_zvals.len(); if num_args < min_num_args || num_args > max_num_args { - // SAFETY: Exported C function is safe, return value is unused and parameters are copied. + // SAFETY: Exported C function is safe, return value is unused and parameters + // are copied. unsafe { zend_wrong_parameters_count_error(min_num_args as _, max_num_args as _) }; return Err(Error::IncorrectArguments(num_args, min_num_args)); } diff --git a/src/binary.rs b/src/binary.rs index 0e72da26a9..60588f58a4 100644 --- a/src/binary.rs +++ b/src/binary.rs @@ -1,5 +1,5 @@ -//! Provides implementations for converting to and from Zend binary strings, commonly returned -//! from functions such as [`pack`] and [`unpack`]. +//! Provides implementations for converting to and from Zend binary strings, +//! commonly returned from functions such as [`pack`] and [`unpack`]. //! //! [`pack`]: https://www.php.net/manual/en/function.pack.php //! [`unpack`]: https://www.php.net/manual/en/function.unpack.php @@ -19,14 +19,16 @@ use crate::{ types::Zval, }; -/// Acts as a wrapper around [`Vec`] where `T` implements [`Pack`]. Primarily used for passing -/// binary data into Rust functions. Can be treated as a [`Vec`] in most situations, or can be -/// 'unwrapped' into a [`Vec`] through the [`From`] implementation on [`Vec`]. +/// Acts as a wrapper around [`Vec`] where `T` implements [`Pack`]. Primarily +/// used for passing binary data into Rust functions. Can be treated as a +/// [`Vec`] in most situations, or can be 'unwrapped' into a [`Vec`] through the +/// [`From`] implementation on [`Vec`]. #[derive(Debug)] pub struct Binary(Vec); impl Binary { - /// Creates a new binary wrapper from a set of data which can be converted into a vector. + /// Creates a new binary wrapper from a set of data which can be converted + /// into a vector. /// /// # Parameters /// @@ -93,19 +95,20 @@ impl FromIterator for Binary { } } -/// Used to convert between Zend binary strings and vectors. Useful in conjunction with the -/// [`pack`] and [`unpack`] functions built-in to PHP. +/// Used to convert between Zend binary strings and vectors. Useful in +/// conjunction with the [`pack`] and [`unpack`] functions built-in to PHP. /// /// # Safety /// -/// The types cannot be ensured between PHP and Rust, as the data is represented as a string when -/// crossing the language boundary. Exercise caution when using these functions. +/// The types cannot be ensured between PHP and Rust, as the data is represented +/// as a string when crossing the language boundary. Exercise caution when using +/// these functions. /// /// [`pack`]: https://www.php.net/manual/en/function.pack.php /// [`unpack`]: https://www.php.net/manual/en/function.unpack.php pub unsafe trait Pack: Clone { - /// Packs a given vector into a Zend binary string. Can be passed to PHP and then unpacked - /// using the [`unpack`] function. + /// Packs a given vector into a Zend binary string. Can be passed to PHP and + /// then unpacked using the [`unpack`] function. /// /// # Parameters /// @@ -114,15 +117,17 @@ pub unsafe trait Pack: Clone { /// [`unpack`]: https://www.php.net/manual/en/function.unpack.php fn pack_into(vec: Vec) -> *mut zend_string; - /// Unpacks a given Zend binary string into a Rust vector. Can be used to pass data from `pack` - /// in PHP to Rust without encoding into another format. Note that the data *must* be all one - /// type, as this implementation only unpacks one type. + /// Unpacks a given Zend binary string into a Rust vector. Can be used to + /// pass data from `pack` in PHP to Rust without encoding into another + /// format. Note that the data *must* be all one type, as this + /// implementation only unpacks one type. /// /// # Safety /// - /// There is no way to tell if the data stored in the string is actually of the given type. - /// The results of this function can also differ from platform-to-platform due to the different - /// representation of some types on different platforms. Consult the [`pack`] function + /// There is no way to tell if the data stored in the string is actually of + /// the given type. The results of this function can also differ from + /// platform-to-platform due to the different representation of some + /// types on different platforms. Consult the [`pack`] function /// documentation for more details. /// /// # Parameters @@ -153,8 +158,9 @@ macro_rules! pack_impl { let mut result = Vec::with_capacity(len as _); let ptr = s.val.as_ptr() as *const $t; - // SAFETY: We calculate the length of memory that we can legally read based on the - // side of the type, therefore we never read outside the memory we should. + // SAFETY: We calculate the length of memory that we can legally read based on + // the side of the type, therefore we never read outside the memory we + // should. for i in 0..len { result.push(unsafe { *ptr.offset(i as _) }); } diff --git a/src/boxed.rs b/src/boxed.rs index 7db06d0774..38623cf5d7 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -1,17 +1,21 @@ //! A pointer type for heap allocation using the Zend memory manager. //! -//! Heap memory in PHP is usually request-bound and allocated inside [memory arenas], which are cleared -//! at the start and end of a PHP request. Allocating and freeing memory that is allocated on the Zend -//! heap is done through two separate functions [`efree`] and [`emalloc`]. +//! Heap memory in PHP is usually request-bound and allocated inside [memory +//! arenas], which are cleared at the start and end of a PHP request. Allocating +//! and freeing memory that is allocated on the Zend heap is done through two +//! separate functions [`efree`] and [`emalloc`]. //! -//! As such, most heap-allocated PHP types **cannot** be allocated on the stack, such as [`ZendStr`], which -//! is a dynamically-sized type, and therefore must be allocated on the heap. A regular [`Box`] would not -//! work in this case, as the memory needs to be freed from a separate function `zend_string_release`. The -//! [`ZBox`] type provides a wrapper which calls the relevant release functions based on the type and what is -//! inside the implementation of [`ZBoxable`]. +//! As such, most heap-allocated PHP types **cannot** be allocated on the stack, +//! such as [`ZendStr`], which is a dynamically-sized type, and therefore must +//! be allocated on the heap. A regular [`Box`] would not work in this case, as +//! the memory needs to be freed from a separate function `zend_string_release`. +//! The [`ZBox`] type provides a wrapper which calls the relevant release +//! functions based on the type and what is inside the implementation of +//! [`ZBoxable`]. //! -//! This type is not created directly, but rather through a function implemented on the downstream type. For -//! example, [`ZendStr`] has a function `new` which returns a [`ZBox`]. +//! This type is not created directly, but rather through a function implemented +//! on the downstream type. For example, [`ZendStr`] has a function `new` which +//! returns a [`ZBox`]. //! //! [memory arenas]: https://en.wikipedia.org/wiki/Region-based_memory_management //! [`ZendStr`]: super::types::string::ZendStr @@ -41,21 +45,24 @@ impl ZBox { /// /// # Safety /// - /// Caller must ensure that `ptr` is non-null, well-aligned and pointing to a `T`. + /// Caller must ensure that `ptr` is non-null, well-aligned and pointing to + /// a `T`. pub unsafe fn from_raw(ptr: *mut T) -> Self { Self(NonNull::new_unchecked(ptr)) } - /// Returns the pointer contained by the box, dropping the box in the process. The data pointed to by - /// the returned pointer is not released. + /// Returns the pointer contained by the box, dropping the box in the + /// process. The data pointed to by the returned pointer is not + /// released. /// /// # Safety /// - /// The caller is responsible for managing the memory pointed to by the returned pointer, including - /// freeing the memory. + /// The caller is responsible for managing the memory pointed to by the + /// returned pointer, including freeing the memory. pub fn into_raw(self) -> &'static mut T { let mut this = ManuallyDrop::new(self); - // SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable. + // SAFETY: All constructors ensure the contained pointer is well-aligned and + // dereferencable. unsafe { this.0.as_mut() } } } @@ -72,7 +79,8 @@ impl Deref for ZBox { #[inline] fn deref(&self) -> &Self::Target { - // SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable. + // SAFETY: All constructors ensure the contained pointer is well-aligned and + // dereferencable. unsafe { self.0.as_ref() } } } @@ -80,7 +88,8 @@ impl Deref for ZBox { impl DerefMut for ZBox { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable. + // SAFETY: All constructors ensure the contained pointer is well-aligned and + // dereferencable. unsafe { self.0.as_mut() } } } @@ -106,20 +115,23 @@ impl AsRef for ZBox { } } -/// Implemented on types that can be heap allocated using the Zend memory manager. These types are stored -/// inside a [`ZBox`] when heap-allocated, and the [`free`] method is called when the box is dropped. +/// Implemented on types that can be heap allocated using the Zend memory +/// manager. These types are stored inside a [`ZBox`] when heap-allocated, and +/// the [`free`] method is called when the box is dropped. /// /// # Safety /// -/// The default implementation of the [`free`] function uses the [`efree`] function to free the memory without -/// calling any destructors. +/// The default implementation of the [`free`] function uses the [`efree`] +/// function to free the memory without calling any destructors. /// -/// The implementor must ensure that any time a pointer to the implementor is passed into a [`ZBox`] that the -/// memory pointed to was allocated by the Zend memory manager. +/// The implementor must ensure that any time a pointer to the implementor is +/// passed into a [`ZBox`] that the memory pointed to was allocated by the Zend +/// memory manager. /// /// [`free`]: #method.free pub unsafe trait ZBoxable { - /// Frees the memory pointed to by `self`, calling any destructors required in the process. + /// Frees the memory pointed to by `self`, calling any destructors required + /// in the process. fn free(&mut self) { unsafe { efree(self as *mut _ as *mut u8) }; } diff --git a/src/builders/class.rs b/src/builders/class.rs index 455748b5a6..04bc35db15 100644 --- a/src/builders/class.rs +++ b/src/builders/class.rs @@ -36,9 +36,9 @@ impl ClassBuilder { /// * `name` - The name of the class. #[allow(clippy::unwrap_used)] pub fn new>(name: T) -> Self { - // SAFETY: Allocating temporary class entry. Will return a null-ptr if allocation fails, - // which will cause the program to panic (standard in Rust). Unwrapping is OK - the ptr - // will either be valid or null. + // SAFETY: Allocating temporary class entry. Will return a null-ptr if + // allocation fails, which will cause the program to panic (standard in + // Rust). Unwrapping is OK - the ptr will either be valid or null. let ptr = unsafe { (std::alloc::alloc_zeroed(Layout::new::()) as *mut ClassEntry) .as_mut() @@ -97,8 +97,9 @@ impl ClassBuilder { self } - /// Adds a property to the class. The initial type of the property is given by the type - /// of the given default. Note that the user can change the type. + /// Adds a property to the class. The initial type of the property is given + /// by the type of the given default. Note that the user can change the + /// type. /// /// # Parameters /// @@ -108,7 +109,8 @@ impl ClassBuilder { /// /// # Panics /// - /// Function will panic if the given `default` cannot be converted into a [`Zval`]. + /// Function will panic if the given `default` cannot be converted into a + /// [`Zval`]. pub fn property>( mut self, name: T, @@ -124,10 +126,11 @@ impl ClassBuilder { self } - /// Adds a constant to the class. The type of the constant is defined by the type of the given - /// default. + /// Adds a constant to the class. The type of the constant is defined by the + /// type of the given default. /// - /// Returns a result containing the class builder if the constant was successfully added. + /// Returns a result containing the class builder if the constant was + /// successfully added. /// /// # Parameters /// @@ -150,22 +153,24 @@ impl ClassBuilder { self } - /// Overrides the creation of the Zend object which will represent an instance - /// of this class. + /// Overrides the creation of the Zend object which will represent an + /// instance of this class. /// /// # Parameters /// - /// * `T` - The type which will override the Zend object. Must implement [`RegisteredClass`] - /// which can be derived using the [`php_class`](crate::php_class) attribute macro. + /// * `T` - The type which will override the Zend object. Must implement + /// [`RegisteredClass`] + /// which can be derived using the [`php_class`](crate::php_class) attribute + /// macro. /// /// # Panics /// - /// Panics if the class name associated with `T` is not the same as the class name specified - /// when creating the builder. + /// Panics if the class name associated with `T` is not the same as the + /// class name specified when creating the builder. pub fn object_override(mut self) -> Self { extern "C" fn create_object(_: *mut ClassEntry) -> *mut ZendObject { - // SAFETY: After calling this function, PHP will always call the constructor defined below, - // which assumes that the object is uninitialized. + // SAFETY: After calling this function, PHP will always call the constructor + // defined below, which assumes that the object is uninitialized. let obj = unsafe { ZendClassObject::::new_uninit() }; obj.into_raw().get_mut_zend_obj() } @@ -244,7 +249,8 @@ impl ClassBuilder { .ok_or(Error::InvalidPointer)? }; - // SAFETY: We allocated memory for this pointer in `new`, so it is our job to free it when the builder has finished. + // SAFETY: We allocated memory for this pointer in `new`, so it is our job to + // free it when the builder has finished. unsafe { std::alloc::dealloc((self.ptr as *mut _) as *mut u8, Layout::new::()) }; diff --git a/src/builders/function.rs b/src/builders/function.rs index d7d5b3e51e..f57797d124 100644 --- a/src/builders/function.rs +++ b/src/builders/function.rs @@ -32,7 +32,8 @@ impl<'a> FunctionBuilder<'a> { /// # Parameters /// /// * `name` - The name of the function. - /// * `handler` - The handler to be called when the function is invoked from PHP. + /// * `handler` - The handler to be called when the function is invoked from + /// PHP. pub fn new>(name: T, handler: FunctionHandler) -> Self { Self { name: name.into(), @@ -58,7 +59,8 @@ impl<'a> FunctionBuilder<'a> { /// /// # Parameters /// - /// * `handler` - The handler to be called when the function is invoked from PHP. + /// * `handler` - The handler to be called when the function is invoked from + /// PHP. pub fn constructor(handler: FunctionHandler) -> Self { Self::new("__construct", handler) } diff --git a/src/builders/module.rs b/src/builders/module.rs index 18a0635c6b..8d31f09191 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -9,8 +9,8 @@ use std::{ mem, ptr, }; -/// Builds a Zend extension. Must be called from within an external function called `get_module`, -/// returning a mutable pointer to a `ModuleEntry`. +/// Builds a Zend extension. Must be called from within an external function +/// called `get_module`, returning a mutable pointer to a `ModuleEntry`. /// /// ``` /// use ext_php_rs::{ @@ -49,7 +49,8 @@ impl ModuleBuilder { /// # Arguments /// /// * `name` - The name of the extension. - /// * `version` - The current version of the extension. TBD: Deprecate in favour of the `Cargo.toml` version? + /// * `version` - The current version of the extension. TBD: Deprecate in + /// favour of the `Cargo.toml` version? pub fn new, U: Into>(name: T, version: U) -> Self { Self { name: name.into(), @@ -131,7 +132,8 @@ impl ModuleBuilder { /// /// # Arguments /// - /// * `func` - The function to be called to retrieve the information about the extension. + /// * `func` - The function to be called to retrieve the information about + /// the extension. pub fn info_function(mut self, func: InfoFunc) -> Self { self.module.info_func = Some(func); self diff --git a/src/class.rs b/src/class.rs index a2540393e4..70a006b96d 100644 --- a/src/class.rs +++ b/src/class.rs @@ -14,8 +14,8 @@ use crate::{ zend::{ClassEntry, ExecutionData, ZendObjectHandlers}, }; -/// Implemented on Rust types which are exported to PHP. Allows users to get and set PHP properties on -/// the object. +/// Implemented on Rust types which are exported to PHP. Allows users to get and +/// set PHP properties on the object. pub trait RegisteredClass: Sized where Self: 'static, @@ -26,10 +26,11 @@ where /// Optional class constructor. const CONSTRUCTOR: Option> = None; - /// Returns a reference to the class metadata, which stores the class entry and handlers. + /// Returns a reference to the class metadata, which stores the class entry + /// and handlers. /// - /// This must be statically allocated, and is usually done through the [`macro@php_class`] - /// macro. + /// This must be statically allocated, and is usually done through the + /// [`macro@php_class`] macro. /// /// [`macro@php_class`]: crate::php_class fn get_metadata() -> &'static ClassMetadata; @@ -42,13 +43,14 @@ where /// /// # Returns /// - /// Returns a given type `T` inside an option which is the value of the zval, or [`None`] - /// if the property could not be found. + /// Returns a given type `T` inside an option which is the value of the + /// zval, or [`None`] if the property could not be found. /// /// # Safety /// - /// Caller must guarantee that the object the function is called on is immediately followed - /// by a [`zend_object`], which is true when the object was instantiated by PHP. + /// Caller must guarantee that the object the function is called on is + /// immediately followed by a [`zend_object`], which is true when the + /// object was instantiated by PHP. unsafe fn get_property<'a, T: FromZval<'a>>(&'a self, name: &str) -> Option { let obj = ZendClassObject::::from_obj_ptr(self)?; obj.std.get_property(name).ok() @@ -63,13 +65,14 @@ where /// /// # Returns /// - /// Returns nothing in an option if the property was successfully set. Returns none if setting - /// the value failed. + /// Returns nothing in an option if the property was successfully set. + /// Returns none if setting the value failed. /// /// # Safety /// - /// Caller must guarantee that the object the function is called on is immediately followed - /// by a [`zend_object`], which is true when the object was instantiated by PHP. + /// Caller must guarantee that the object the function is called on is + /// immediately followed by a [`zend_object`], which is true when the + /// object was instantiated by PHP. unsafe fn set_property(&mut self, name: &str, value: impl IntoZval) -> Option<()> { let obj = ZendClassObject::::from_obj_ptr(self)?; obj.std.set_property(name, value).ok()?; @@ -78,8 +81,9 @@ where /// Returns a hash table containing the properties of the class. /// - /// The key should be the name of the property and the value should be a reference to the property - /// with reference to `self`. The value is a [`Property`]. + /// The key should be the name of the property and the value should be a + /// reference to the property with reference to `self`. The value is a + /// [`Property`]. fn get_properties<'a>() -> HashMap<&'static str, Property<'a, Self>>; } @@ -87,7 +91,8 @@ where pub struct ConstructorMeta { /// Constructor function. pub constructor: fn(&mut ExecutionData) -> ConstructorResult, - /// Function called to build the constructor function. Usually adds arguments. + /// Function called to build the constructor function. Usually adds + /// arguments. pub build_fn: fn(FunctionBuilder) -> FunctionBuilder, } @@ -119,7 +124,8 @@ impl From for ConstructorResult { } } -/// Stores the class entry and handlers for a Rust type which has been exported to PHP. +/// Stores the class entry and handlers for a Rust type which has been exported +/// to PHP. pub struct ClassMetadata { handlers_init: AtomicBool, handlers: MaybeUninit, @@ -141,7 +147,8 @@ impl ClassMetadata { } impl ClassMetadata { - /// Returns an immutable reference to the object handlers contained inside the class metadata. + /// Returns an immutable reference to the object handlers contained inside + /// the class metadata. pub fn handlers(&self) -> &ZendObjectHandlers { self.check_handlers(); @@ -160,8 +167,9 @@ impl ClassMetadata { /// /// Panics if there is no class entry stored inside the class metadata. pub fn ce(&self) -> &'static ClassEntry { - // SAFETY: There are only two values that can be stored in the atomic ptr: null or a static reference - // to a class entry. On the latter case, `as_ref()` will return `None` and the function will panic. + // SAFETY: There are only two values that can be stored in the atomic ptr: null + // or a static reference to a class entry. On the latter case, + // `as_ref()` will return `None` and the function will panic. unsafe { self.ce.load(Ordering::SeqCst).as_ref() } .expect("Attempted to retrieve class entry before it has been stored.") } @@ -174,8 +182,8 @@ impl ClassMetadata { /// /// # Panics /// - /// Panics if the class entry has already been set in the class metadata. This function should - /// only be called once. + /// Panics if the class entry has already been set in the class metadata. + /// This function should only be called once. pub fn set_ce(&self, ce: &'static mut ClassEntry) { if !self.ce.load(Ordering::SeqCst).is_null() { panic!("Class entry has already been set."); @@ -184,7 +192,8 @@ impl ClassMetadata { self.ce.store(ce, Ordering::SeqCst); } - /// Checks if the handlers have been initialized, and initializes them if they are not. + /// Checks if the handlers have been initialized, and initializes them if + /// they are not. fn check_handlers(&self) { if !self.handlers_init.load(Ordering::Acquire) { // SAFETY: `MaybeUninit` has the same size as the handlers. diff --git a/src/closure.rs b/src/closure.rs index 41dd829ce4..1ca93a4711 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -19,12 +19,12 @@ static CLOSURE_META: ClassMetadata = ClassMetadata::new(); /// Wrapper around a Rust closure, which can be exported to PHP. /// -/// Closures can have up to 8 parameters, all must implement [`FromZval`], and can return anything -/// that implements [`IntoZval`]. Closures must have a static lifetime, and therefore cannot modify -/// any `self` references. +/// Closures can have up to 8 parameters, all must implement [`FromZval`], and +/// can return anything that implements [`IntoZval`]. Closures must have a +/// static lifetime, and therefore cannot modify any `self` references. /// -/// Internally, closures are implemented as a PHP class. A class `RustClosure` is registered with an -/// `__invoke` method: +/// Internally, closures are implemented as a PHP class. A class `RustClosure` +/// is registered with an `__invoke` method: /// /// ```php /// = ClassMetadata::new(); /// } /// ``` /// -/// The Rust closure is then double boxed, firstly as a `Box ...>` (depending on the -/// signature of the closure) and then finally boxed as a `Box`. This is a workaround, -/// as `PhpClosure` is not generically implementable on types that implement `Fn(T, ...) -> Ret`. Make +/// The Rust closure is then double boxed, firstly as a `Box +/// ...>` (depending on the signature of the closure) and then finally boxed as +/// a `Box`. This is a workaround, as `PhpClosure` is not +/// generically implementable on types that implement `Fn(T, ...) -> Ret`. Make /// a suggestion issue if you have a better idea of implementing this!. /// -/// When the `__invoke` method is called from PHP, the `invoke` method is called on the `dyn PhpClosure`\ -/// trait object, and from there everything is basically the same as a regular PHP function. +/// When the `__invoke` method is called from PHP, the `invoke` method is called +/// on the `dyn PhpClosure`\ trait object, and from there everything is +/// basically the same as a regular PHP function. pub struct Closure(Box); unsafe impl Send for Closure {} unsafe impl Sync for Closure {} impl Closure { - /// Wraps a [`Fn`] or [`FnMut`] Rust closure into a type which can be returned to PHP. + /// Wraps a [`Fn`] or [`FnMut`] Rust closure into a type which can be + /// returned to PHP. /// - /// The closure can accept up to 8 arguments which implement [`IntoZval`], and can return any - /// type which implements [`FromZval`]. The closure must have a static lifetime, so cannot - /// reference `self`. + /// The closure can accept up to 8 arguments which implement [`IntoZval`], + /// and can return any type which implements [`FromZval`]. The closure + /// must have a static lifetime, so cannot reference `self`. /// /// # Parameters /// - /// * `func` - The closure to wrap. Should be boxed in the form `Box ...>`. + /// * `func` - The closure to wrap. Should be boxed in the form `Box ...>`. /// /// # Example /// @@ -75,16 +79,18 @@ impl Closure { Self(Box::new(func) as Box) } - /// Wraps a [`FnOnce`] Rust closure into a type which can be returned to PHP. If the closure - /// is called more than once from PHP, an exception is thrown. + /// Wraps a [`FnOnce`] Rust closure into a type which can be returned to + /// PHP. If the closure is called more than once from PHP, an exception + /// is thrown. /// - /// The closure can accept up to 8 arguments which implement [`IntoZval`], and can return any - /// type which implements [`FromZval`]. The closure must have a static lifetime, so cannot - /// reference `self`. + /// The closure can accept up to 8 arguments which implement [`IntoZval`], + /// and can return any type which implements [`FromZval`]. The closure + /// must have a static lifetime, so cannot reference `self`. /// /// # Parameters /// - /// * `func` - The closure to wrap. Should be boxed in the form `Box ...>`. + /// * `func` - The closure to wrap. Should be boxed in the form `Box ...>`. /// /// # Example /// @@ -103,8 +109,9 @@ impl Closure { func.into_closure() } - /// Builds the class entry for [`Closure`], registering it with PHP. This function should - /// only be called once inside your module startup function. + /// Builds the class entry for [`Closure`], registering it with PHP. This + /// function should only be called once inside your module startup + /// function. /// /// # Panics /// @@ -155,22 +162,25 @@ class_derives!(Closure); /// Implemented on types which can be used as PHP closures. /// -/// Types must implement the `invoke` function which will be called when the closure is called -/// from PHP. Arguments must be parsed from the [`ExecutionData`] and the return value is returned -/// through the [`Zval`]. +/// Types must implement the `invoke` function which will be called when the +/// closure is called from PHP. Arguments must be parsed from the +/// [`ExecutionData`] and the return value is returned through the [`Zval`]. /// -/// This trait is automatically implemented on functions with up to 8 parameters. +/// This trait is automatically implemented on functions with up to 8 +/// parameters. pub unsafe trait PhpClosure { /// Invokes the closure. fn invoke<'a>(&'a mut self, parser: ArgParser<'a, '_>, ret: &mut Zval); } -/// Implemented on [`FnOnce`] types which can be used as PHP closures. See [`Closure`]. +/// Implemented on [`FnOnce`] types which can be used as PHP closures. See +/// [`Closure`]. /// -/// Internally, this trait should wrap the [`FnOnce`] closure inside a [`FnMut`] closure, and prevent -/// the user from calling the closure more than once. +/// Internally, this trait should wrap the [`FnOnce`] closure inside a [`FnMut`] +/// closure, and prevent the user from calling the closure more than once. pub trait PhpOnceClosure { - /// Converts the Rust [`FnOnce`] closure into a [`FnMut`] closure, and then into a PHP closure. + /// Converts the Rust [`FnOnce`] closure into a [`FnMut`] closure, and then + /// into a PHP closure. fn into_closure(self) -> Closure; } diff --git a/src/constant.rs b/src/constant.rs index bc33f0f081..fda5e446f6 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -10,18 +10,21 @@ use crate::ffi::{ }; pub trait IntoConst: Sized { - /// Registers a global module constant in PHP, with the value as the content of self. - /// This function _must_ be called in the module startup function, which is called after - /// the module is initialized. The second parameter of the startup function will be the - /// module number. By default, the case-insensitive and persistent flags are set when + /// Registers a global module constant in PHP, with the value as the content + /// of self. This function _must_ be called in the module startup + /// function, which is called after the module is initialized. The + /// second parameter of the startup function will be the module number. + /// By default, the case-insensitive and persistent flags are set when /// registering the constant. /// - /// Returns a result containing nothing if the constant was successfully registered. + /// Returns a result containing nothing if the constant was successfully + /// registered. /// /// # Parameters /// /// * `name` - The name of the constant. - /// * `module_number` - The module number that we are registering the constant under. + /// * `module_number` - The module number that we are registering the + /// constant under. /// /// # Examples /// @@ -42,19 +45,22 @@ pub trait IntoConst: Sized { ) } - /// Registers a global module constant in PHP, with the value as the content of self. - /// This function _must_ be called in the module startup function, which is called after - /// the module is initialized. The second parameter of the startup function will be the - /// module number. This function allows you to pass any extra flags in if you require. - /// Note that the case-sensitive and persistent flags *are not* set when you use this function, - /// you must set these yourself. + /// Registers a global module constant in PHP, with the value as the content + /// of self. This function _must_ be called in the module startup + /// function, which is called after the module is initialized. The + /// second parameter of the startup function will be the module number. + /// This function allows you to pass any extra flags in if you require. + /// Note that the case-sensitive and persistent flags *are not* set when you + /// use this function, you must set these yourself. /// - /// Returns a result containing nothing if the constant was successfully registered. + /// Returns a result containing nothing if the constant was successfully + /// registered. /// /// # Parameters /// /// * `name` - The name of the constant. - /// * `module_number` - The module number that we are registering the constant under. + /// * `module_number` - The module number that we are registering the + /// constant under. /// * `flags` - Flags to register the constant with. /// /// # Examples @@ -127,7 +133,8 @@ impl IntoConst for bool { } } -/// Implements the `IntoConst` trait for a given number type using a given function. +/// Implements the `IntoConst` trait for a given number type using a given +/// function. macro_rules! into_const_num { ($type: ty, $fn: expr) => { impl IntoConst for $type { diff --git a/src/convert.rs b/src/convert.rs index 33d92af8b4..9db5de7592 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -6,13 +6,14 @@ use crate::{ types::{ZendObject, Zval}, }; -/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal of the [`IntoZval`] -/// trait. +/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal +/// of the [`IntoZval`] trait. pub trait FromZval<'a>: Sized { /// The corresponding type of the implemented value in PHP. const TYPE: DataType; - /// Attempts to retrieve an instance of `Self` from a reference to a [`Zval`]. + /// Attempts to retrieve an instance of `Self` from a reference to a + /// [`Zval`]. /// /// # Parameters /// @@ -33,14 +34,15 @@ where /// Allows mutable zvals to be converted into Rust types in a fallible way. /// -/// If `Self` does not require the zval to be mutable to be extracted, you should implement -/// [`FromZval`] instead, as this trait is generically implemented for any type that implements -/// [`FromZval`]. +/// If `Self` does not require the zval to be mutable to be extracted, you +/// should implement [`FromZval`] instead, as this trait is generically +/// implemented for any type that implements [`FromZval`]. pub trait FromZvalMut<'a>: Sized { /// The corresponding type of the implemented value in PHP. const TYPE: DataType; - /// Attempts to retrieve an instance of `Self` from a mutable reference to a [`Zval`]. + /// Attempts to retrieve an instance of `Self` from a mutable reference to a + /// [`Zval`]. /// /// # Parameters /// @@ -60,7 +62,8 @@ where } } -/// `FromZendObject` is implemented by types which can be extracted from a Zend object. +/// `FromZendObject` is implemented by types which can be extracted from a Zend +/// object. /// /// Normal usage is through the helper method `ZendObject::extract`: /// @@ -70,7 +73,8 @@ where /// let props: HashMap = obj.extract(); /// ``` /// -/// Should be functionally equivalent to casting an object to another compatable type. +/// Should be functionally equivalent to casting an object to another compatable +/// type. pub trait FromZendObject<'a>: Sized { /// Extracts `Self` from the source `ZendObject`. fn from_zend_object(obj: &'a ZendObject) -> Result; @@ -96,39 +100,42 @@ where } } -/// Implemented on types which can be converted into a Zend object. It is up to the implementation -/// to determine the type of object which is produced. +/// Implemented on types which can be converted into a Zend object. It is up to +/// the implementation to determine the type of object which is produced. pub trait IntoZendObject { /// Attempts to convert `self` into a Zend object. fn into_zend_object(self) -> Result>; } -/// Provides implementations for converting Rust primitive types into PHP zvals. Alternative to the -/// built-in Rust [`From`] and [`TryFrom`] implementations, allowing the caller to specify whether -/// the Zval contents will persist between requests. +/// Provides implementations for converting Rust primitive types into PHP zvals. +/// Alternative to the built-in Rust [`From`] and [`TryFrom`] implementations, +/// allowing the caller to specify whether the Zval contents will persist +/// between requests. pub trait IntoZval: Sized { /// The corresponding type of the implemented value in PHP. const TYPE: DataType; - /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if - /// successful. + /// Converts a Rust primitive type into a Zval. Returns a result containing + /// the Zval if successful. /// /// # Parameters /// - /// * `persistent` - Whether the contents of the Zval will persist between requests. + /// * `persistent` - Whether the contents of the Zval will persist between + /// requests. fn into_zval(self, persistent: bool) -> Result { let mut zval = Zval::new(); self.set_zval(&mut zval, persistent)?; Ok(zval) } - /// Sets the content of a pre-existing zval. Returns a result containing nothing if setting - /// the content was successful. + /// Sets the content of a pre-existing zval. Returns a result containing + /// nothing if setting the content was successful. /// /// # Parameters /// /// * `zv` - The Zval to set the content of. - /// * `persistent` - Whether the contents of the Zval will persist between requests. + /// * `persistent` - Whether the contents of the Zval will persist between + /// requests. fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()>; } @@ -180,15 +187,18 @@ where /// An object-safe version of the [`IntoZval`] trait. /// -/// This trait is automatically implemented on any type that implements both [`IntoZval`] and [`Clone`]. -/// You avoid implementing this trait directly, rather implement these two other traits. +/// This trait is automatically implemented on any type that implements both +/// [`IntoZval`] and [`Clone`]. You avoid implementing this trait directly, +/// rather implement these two other traits. pub trait IntoZvalDyn { - /// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if - /// successful. `self` is cloned before being converted into a zval. + /// Converts a Rust primitive type into a Zval. Returns a result containing + /// the Zval if successful. `self` is cloned before being converted into + /// a zval. /// /// # Parameters /// - /// * `persistent` - Whether the contents of the Zval will persist between requests. + /// * `persistent` - Whether the contents of the Zval will persist between + /// requests. fn as_zval(&self, persistent: bool) -> Result; /// Returns the PHP type of the type. diff --git a/src/error.rs b/src/error.rs index 41b296288e..15e77def60 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,16 +32,19 @@ pub enum Error { /// Attempted to convert a [`ZvalTypeFlags`] struct to a [`DataType`]. /// The flags did not contain a datatype. /// - /// The enum carries the flags that were attempted to be converted to a [`DataType`]. + /// The enum carries the flags that were attempted to be converted to a + /// [`DataType`]. InvalidTypeToDatatype(ZvalTypeFlags), - /// The function called was called in an invalid scope (calling class-related functions - /// inside of a non-class bound function). + /// The function called was called in an invalid scope (calling + /// class-related functions inside of a non-class bound function). InvalidScope, - /// The pointer inside a given type was invalid, either null or pointing to garbage. + /// The pointer inside a given type was invalid, either null or pointing to + /// garbage. InvalidPointer, /// The given property name does not exist. InvalidProperty, - /// The string could not be converted into a C-string due to the presence of a NUL character. + /// The string could not be converted into a C-string due to the presence of + /// a NUL character. InvalidCString, /// Could not call the given function. Callable, diff --git a/src/exception.rs b/src/exception.rs index cc9a23b974..e622e0748f 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -13,13 +13,13 @@ use crate::{ /// Result type with the error variant as a [`PhpException`]. pub type PhpResult = std::result::Result; -/// Represents a PHP exception which can be thrown using the `throw()` function. Primarily used to -/// return from a [`Result`] which can immediately be thrown by the `ext-php-rs` -/// macro API. +/// Represents a PHP exception which can be thrown using the `throw()` function. +/// Primarily used to return from a [`Result`] which can +/// immediately be thrown by the `ext-php-rs` macro API. /// -/// There are default [`From`] implementations for any type that implements [`ToString`], so these -/// can also be returned from these functions. You can also implement [`From`] for your custom -/// error type. +/// There are default [`From`] implementations for any type that implements +/// [`ToString`], so these can also be returned from these functions. You can +/// also implement [`From`] for your custom error type. #[derive(Debug)] pub struct PhpException { message: String, @@ -39,8 +39,9 @@ impl PhpException { Self { message, code, ex } } - /// Creates a new default exception instance, using the default PHP `Exception` type as the - /// exception type, with an integer code of zero. + /// Creates a new default exception instance, using the default PHP + /// `Exception` type as the exception type, with an integer code of + /// zero. /// /// # Parameters /// @@ -58,8 +59,8 @@ impl PhpException { Self::new(message, 0, T::get_metadata().ce()) } - /// Throws the exception, returning nothing inside a result if successful and an error - /// otherwise. + /// Throws the exception, returning nothing inside a result if successful + /// and an error otherwise. pub fn throw(self) -> Result<()> { throw_with_code(self.ex, self.code, &self.message) } @@ -77,8 +78,8 @@ impl From<&str> for PhpException { } } -/// Throws an exception with a given message. See [`ClassEntry`] for some built-in exception -/// types. +/// Throws an exception with a given message. See [`ClassEntry`] for some +/// built-in exception types. /// /// Returns a result containing nothing if the exception was successfully thown. /// @@ -98,8 +99,8 @@ pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> { throw_with_code(ex, 0, message) } -/// Throws an exception with a given message and status code. See [`ClassEntry`] for some built-in -/// exception types. +/// Throws an exception with a given message and status code. See [`ClassEntry`] +/// for some built-in exception types. /// /// Returns a result containing nothing if the exception was successfully thown. /// @@ -124,8 +125,8 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> return Err(Error::InvalidException(flags)); } - // SAFETY: We are given a reference to a `ClassEntry` therefore when we cast it to a pointer it - // will be valid. + // SAFETY: We are given a reference to a `ClassEntry` therefore when we cast it + // to a pointer it will be valid. unsafe { zend_throw_exception_ex( (ex as *const _) as *mut _, diff --git a/src/internal.rs b/src/internal.rs index a395fddfdd..bc581f85ef 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,5 +1,5 @@ -/// Called by startup functions registered with the `#[php_startup]` macro. Initializes all -/// classes that are defined by ext-php-rs (i.e. [`Closure`]). +/// Called by startup functions registered with the `#[php_startup]` macro. +/// Initializes all classes that are defined by ext-php-rs (i.e. [`Closure`]). /// /// [`Closure`]: ext_php_rs::php::types::closure::Closure #[inline(always)] diff --git a/src/lib.rs b/src/lib.rs index 8e9f9fefc8..ef9cb314a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,8 @@ pub mod rc; pub mod types; pub mod zend; -/// A module typically glob-imported containing the typically required macros and imports. +/// A module typically glob-imported containing the typically required macros +/// and imports. pub mod prelude { pub use crate::builders::ModuleBuilder; #[cfg(any(docs, feature = "closure"))] @@ -49,11 +50,13 @@ pub mod prelude { /// Attribute used to annotate constants to be exported to PHP. /// -/// The declared constant is left intact (apart from the addition of the `#[allow(dead_code)]` -/// attribute in the case that you do not use the Rust constant). +/// The declared constant is left intact (apart from the addition of the +/// `#[allow(dead_code)]` attribute in the case that you do not use the Rust +/// constant). /// -/// These declarations must happen before you declare your [`macro@php_startup`] function (or -/// [`macro@php_module`] function if you do not have a startup function). +/// These declarations must happen before you declare your [`macro@php_startup`] +/// function (or [`macro@php_module`] function if you do not have a startup +/// function). /// /// # Example /// @@ -71,12 +74,14 @@ pub mod prelude { /// ``` pub use ext_php_rs_derive::php_const; -/// Attribute used to annotate `extern` blocks which are deemed as PHP functions. +/// Attribute used to annotate `extern` blocks which are deemed as PHP +/// functions. /// -/// This allows you to 'import' PHP functions into Rust so that they can be called like regular -/// Rust functions. Parameters can be any type that implements [`IntoZval`], and the return type -/// can be anything that implements [`From`] (notice how [`Zval`] is consumed rather than -/// borrowed in this case). +/// This allows you to 'import' PHP functions into Rust so that they can be +/// called like regular Rust functions. Parameters can be any type that +/// implements [`IntoZval`], and the return type can be anything that implements +/// [`From`] (notice how [`Zval`] is consumed rather than borrowed in this +/// case). /// /// # Panics /// @@ -87,15 +92,17 @@ pub use ext_php_rs_derive::php_const; /// * The actual function call failed internally. /// * The output [`Zval`] could not be parsed into the output type. /// -/// The last point can be important when interacting with functions that return unions, such as -/// [`strpos`] which can return an integer or a boolean. In this case, a [`Zval`] should be -/// returned as parsing a boolean to an integer is invalid, and vice versa. +/// The last point can be important when interacting with functions that return +/// unions, such as [`strpos`] which can return an integer or a boolean. In this +/// case, a [`Zval`] should be returned as parsing a boolean to an integer is +/// invalid, and vice versa. /// /// # Example /// -/// This `extern` block imports the [`strpos`] function from PHP. Notice that the string parameters -/// can take either [`String`] or [`&str`], the optional parameter `offset` is an [`Option`], -/// and the return value is a [`Zval`] as the return type is an integer-boolean union. +/// This `extern` block imports the [`strpos`] function from PHP. Notice that +/// the string parameters can take either [`String`] or [`&str`], the optional +/// parameter `offset` is an [`Option`], and the return value is a [`Zval`] +/// as the return type is an integer-boolean union. /// /// ``` /// # use ext_php_rs::prelude::*; @@ -122,38 +129,45 @@ pub use ext_php_rs_derive::php_extern; /// Attribute used to annotate a function as a PHP function. /// -/// Only types that implement [`FromZval`] can be used as parameter and return types. These include -/// but are not limited to the following: +/// Only types that implement [`FromZval`] can be used as parameter and return +/// types. These include but are not limited to the following: /// -/// - Most primitive integers ([`i8`], [`i16`], [`i32`], [`i64`], [`u8`], [`u16`], [`u32`], [`u64`], +/// - Most primitive integers ([`i8`], [`i16`], [`i32`], [`i64`], [`u8`], +/// [`u16`], [`u32`], [`u64`], /// [`usize`], [`isize`]) /// - Double-precision floating point numbers ([`f64`]) /// - [`bool`] /// - [`String`] -/// - [`Vec`] and [`HashMap`](std::collections::HashMap) where `T: FromZval`. +/// - [`Vec`] and [`HashMap`](std::collections::HashMap) where `T: +/// FromZval`. /// - [`Binary`] for passing binary data as a string, where `T: Pack`. -/// - [`Callable`] for receiving PHP callables, not applicable for return values. -/// - [`Option`] where `T: FromZval`. When used as a parameter, the parameter will be -/// deemed nullable, and will contain [`None`] when `null` is passed. When used as a return type, -/// if [`None`] is returned the [`Zval`] will be set to null. Optional parameters *must* be of the -/// type [`Option`]. -/// -/// Additionally, you are able to return a variant of [`Result`]. `T` must implement -/// [`IntoZval`] and `E` must implement `Into`. If an error variant is returned, a -/// PHP exception is thrown using the [`PhpException`] struct contents. -/// -/// You are able to implement [`FromZval`] on your own custom types to have arguments passed in -/// seamlessly. Similarly, you can implement [`IntoZval`] on values that you want to be able to be -/// returned from PHP fucntions. -/// -/// Parameters may be deemed optional by passing the parameter name into the attribute options. -/// Note that all parameters that are optional (which includes the given optional parameter as well -/// as all parameters after) *must* be of the type [`Option`], where `T` is a valid type. +/// - [`Callable`] for receiving PHP callables, not applicable for return +/// values. +/// - [`Option`] where `T: FromZval`. When used as a parameter, the parameter +/// will be +/// deemed nullable, and will contain [`None`] when `null` is passed. When used +/// as a return type, if [`None`] is returned the [`Zval`] will be set to null. +/// Optional parameters *must* be of the type [`Option`]. +/// +/// Additionally, you are able to return a variant of [`Result`]. `T` must +/// implement [`IntoZval`] and `E` must implement `Into`. If an +/// error variant is returned, a PHP exception is thrown using the +/// [`PhpException`] struct contents. +/// +/// You are able to implement [`FromZval`] on your own custom types to have +/// arguments passed in seamlessly. Similarly, you can implement [`IntoZval`] on +/// values that you want to be able to be returned from PHP fucntions. +/// +/// Parameters may be deemed optional by passing the parameter name into the +/// attribute options. Note that all parameters that are optional (which +/// includes the given optional parameter as well as all parameters after) +/// *must* be of the type [`Option`], where `T` is a valid type. /// /// Generics are *not* supported. /// -/// Behind the scenes, an `extern "C"` wrapper function is generated, which is actually called by -/// PHP. The first example function would be converted into a function which looks like so: +/// Behind the scenes, an `extern "C"` wrapper function is generated, which is +/// actually called by PHP. The first example function would be converted into a +/// function which looks like so: /// /// ```no_run /// # use ext_php_rs::{prelude::*, exception::PhpException, zend::ExecutionData, convert::{FromZval, IntoZval}, types::Zval, args::{Arg, ArgParser}}; @@ -191,13 +205,13 @@ pub use ext_php_rs_derive::php_extern; /// } /// ``` /// -/// This allows the original function to continue being used while also being exported as a PHP -/// function. +/// This allows the original function to continue being used while also being +/// exported as a PHP function. /// /// # Examples /// -/// Creating a simple function which will return a string. The function still must be declared in -/// the PHP module to be able to call. +/// Creating a simple function which will return a string. The function still +/// must be declared in the PHP module to be able to call. /// /// ``` /// # use ext_php_rs::prelude::*; @@ -211,9 +225,9 @@ pub use ext_php_rs_derive::php_extern; /// # } /// ``` /// -/// Parameters can also be deemed optional by passing the parameter name in the attribute options. -/// This function takes one required parameter (`hello`) and two optional parameters (`description` -/// and `age`). +/// Parameters can also be deemed optional by passing the parameter name in the +/// attribute options. This function takes one required parameter (`hello`) and +/// two optional parameters (`description` and `age`). /// /// ``` /// # use ext_php_rs::prelude::*; @@ -237,8 +251,9 @@ pub use ext_php_rs_derive::php_extern; /// # } /// ``` /// -/// Defaults can also be given in a similar fashion. For example, the above function could have -/// default values for `description` and `age` by changing the attribute to the following: +/// Defaults can also be given in a similar fashion. For example, the above +/// function could have default values for `description` and `age` by changing +/// the attribute to the following: /// /// ``` /// # use ext_php_rs::prelude::*; @@ -262,44 +277,51 @@ pub use ext_php_rs_derive::php_extern; /// [`PhpException`]: crate::php::exceptions::PhpException pub use ext_php_rs_derive::php_function; -/// Annotates a structs `impl` block, declaring that all methods and constants declared -/// inside the `impl` block will be declared as PHP methods and constants. +/// Annotates a structs `impl` block, declaring that all methods and constants +/// declared inside the `impl` block will be declared as PHP methods and +/// constants. /// -/// If you do not want to export a method to PHP, declare it in another `impl` block that is not -/// tagged with this macro. +/// If you do not want to export a method to PHP, declare it in another `impl` +/// block that is not tagged with this macro. /// -/// The declared methods and functions are kept intact so they can continue to be called from Rust. -/// Methods do generate an additional function, with an identifier in the format -/// `_internal_php_#ident`. +/// The declared methods and functions are kept intact so they can continue to +/// be called from Rust. Methods do generate an additional function, with an +/// identifier in the format `_internal_php_#ident`. /// -/// Methods and constants are declared mostly the same as their global counterparts, so read the -/// documentation on the [`macro@php_function`] and [`macro@php_const`] macros for more details. +/// Methods and constants are declared mostly the same as their global +/// counterparts, so read the documentation on the [`macro@php_function`] and +/// [`macro@php_const`] macros for more details. /// -/// The main difference is that the contents of the `impl` block *do not* need to be tagged with -/// additional attributes - this macro assumes that all contents of the `impl` block are to be -/// exported to PHP. +/// The main difference is that the contents of the `impl` block *do not* need +/// to be tagged with additional attributes - this macro assumes that all +/// contents of the `impl` block are to be exported to PHP. /// -/// The only contrary to this is setting the visibility, optional argument and default arguments -/// for methods. These are done through seperate macros: +/// The only contrary to this is setting the visibility, optional argument and +/// default arguments for methods. These are done through seperate macros: /// -/// - `#[defaults(key = value, ...)]` for setting defaults of method variables, similar to the +/// - `#[defaults(key = value, ...)]` for setting defaults of method variables, +/// similar to the /// function macro. Arguments with defaults need to be optional. -/// - `#[optional(key)]` for setting `key` as an optional argument (and therefore the rest of the +/// - `#[optional(key)]` for setting `key` as an optional argument (and +/// therefore the rest of the /// arguments). -/// - `#[public]`, `#[protected]` and `#[private]` for setting the visibility of the method, -/// defaulting to public. The Rust visibility has no effect on the PHP visibility. +/// - `#[public]`, `#[protected]` and `#[private]` for setting the visibility of +/// the method, +/// defaulting to public. The Rust visibility has no effect on the PHP +/// visibility. /// -/// Methods can take a immutable or a mutable reference to `self`, but cannot consume `self`. They -/// can also take no reference to `self` which indicates a static method. +/// Methods can take a immutable or a mutable reference to `self`, but cannot +/// consume `self`. They can also take no reference to `self` which indicates a +/// static method. /// /// ## Constructors /// -/// You may add *one* constructor to the impl block. This method must be called `__construct` or be -/// tagged with the `#[constructor]` attribute, and it will not be exported to PHP like a regular -/// method. +/// You may add *one* constructor to the impl block. This method must be called +/// `__construct` or be tagged with the `#[constructor]` attribute, and it will +/// not be exported to PHP like a regular method. /// -/// The constructor method must not take a reference to `self` and must return `Self` or -/// [`Result`][`Result`], where `E: Into`. +/// The constructor method must not take a reference to `self` and must return +/// `Self` or [`Result`][`Result`], where `E: Into`. /// /// # Example /// @@ -344,29 +366,33 @@ pub use ext_php_rs_derive::php_function; /// ``` pub use ext_php_rs_derive::php_impl; -/// Annotates a function that will be used by PHP to retrieve information about the module. +/// Annotates a function that will be used by PHP to retrieve information about +/// the module. /// -/// In the process, the function is wrapped by an `extern "C"` function which is called from PHP, -/// which then calls the given function. +/// In the process, the function is wrapped by an `extern "C"` function which is +/// called from PHP, which then calls the given function. /// -/// As well as wrapping the function, the `ModuleBuilder` is initialized and functions which have -/// already been declared with the [`macro@php_function`] attribute will be registered with the -/// module, so ideally you won't have to do anything inside the function. +/// As well as wrapping the function, the `ModuleBuilder` is initialized and +/// functions which have already been declared with the [`macro@php_function`] +/// attribute will be registered with the module, so ideally you won't have to +/// do anything inside the function. /// -/// The attribute must be called on a function *last*, i.e. the last proc-macro to be compiled, as -/// the attribute relies on all other PHP attributes being compiled before the module. If another -/// PHP attribute is compiled after the module attribute, an error will be thrown. +/// The attribute must be called on a function *last*, i.e. the last proc-macro +/// to be compiled, as the attribute relies on all other PHP attributes being +/// compiled before the module. If another PHP attribute is compiled after the +/// module attribute, an error will be thrown. /// /// Note that if the function is not called `get_module`, it will be renamed. /// -/// If you have defined classes using the [`macro@php_class`] macro and you have not defined -/// a startup function, it will be automatically declared and registered. +/// If you have defined classes using the [`macro@php_class`] macro and you have +/// not defined a startup function, it will be automatically declared and +/// registered. /// /// # Example /// -/// The `get_module` function is required in every PHP extension. This is a bare minimum example, -/// since the function is declared above the module it will automatically be registered when the -/// module attribute is called. +/// The `get_module` function is required in every PHP extension. This is a bare +/// minimum example, since the function is declared above the module it will +/// automatically be registered when the module attribute is called. /// /// ``` /// # use ext_php_rs::prelude::*; @@ -384,29 +410,32 @@ pub use ext_php_rs_derive::php_module; /// Annotates a struct that will be exported to PHP as a class. /// -/// By default, the class cannot be constructed from PHP. You must add a constructor method in the -/// [`macro@php_impl`] impl block to be able to construct the object from PHP. +/// By default, the class cannot be constructed from PHP. You must add a +/// constructor method in the [`macro@php_impl`] impl block to be able to +/// construct the object from PHP. /// /// This attribute takes a set of optional arguments: /// -/// * `name` - The name of the exported class, if it is different from the Rust struct name. This -/// can be useful for namespaced classes, as you cannot place backslashes in Rust struct names. +/// * `name` - The name of the exported class, if it is different from the Rust +/// struct name. This can be useful for namespaced classes, as you cannot +/// place backslashes in Rust struct names. /// -/// Any struct that uses this attribute can also provide an optional set of extra attributes, used -/// to modify the class. These attributes must be used **underneath** this attribute, as they are -/// not valid Rust attributes, and instead are parsed by this attribute: +/// Any struct that uses this attribute can also provide an optional set of +/// extra attributes, used to modify the class. These attributes must be used +/// **underneath** this attribute, as they are not valid Rust attributes, and +/// instead are parsed by this attribute: /// -/// * `#[extends(ce)]` - Sets the parent class of this new class. Can only be used once, and `ce` -/// may be any valid expression. -/// * `#[implements(ce)]` - Implements an interface on the new class. Can be used multiple times, -/// and `ce` may be any valid expression. +/// * `#[extends(ce)]` - Sets the parent class of this new class. Can only be +/// used once, and `ce` may be any valid expression. +/// * `#[implements(ce)]` - Implements an interface on the new class. Can be +/// used multiple times, and `ce` may be any valid expression. /// -/// This attribute (and its associated structs) must be defined *above* the startup function (which -/// is annotated by the [`macro@php_startup`] macro, or automatically generated just above the -/// [`macro@php_module`] function). +/// This attribute (and its associated structs) must be defined *above* the +/// startup function (which is annotated by the [`macro@php_startup`] macro, or +/// automatically generated just above the [`macro@php_module`] function). /// -/// Fields defined on the struct *are not* the same as PHP properties, and are only accessible from -/// Rust. +/// Fields defined on the struct *are not* the same as PHP properties, and are +/// only accessible from Rust. /// /// # Example /// @@ -427,7 +456,8 @@ pub use ext_php_rs_derive::php_module; /// } /// ``` /// -/// Create a custom exception `RedisException` inside the namespace `Redis\Exception`: +/// Create a custom exception `RedisException` inside the namespace +/// `Redis\Exception`: /// /// ``` /// # use ext_php_rs::prelude::*; @@ -450,18 +480,19 @@ pub use ext_php_rs_derive::php_module; /// ``` pub use ext_php_rs_derive::php_class; -/// Annotates a function that will be called by PHP when the module starts up. Generally used to -/// register classes and constants. +/// Annotates a function that will be called by PHP when the module starts up. +/// Generally used to register classes and constants. /// -/// As well as annotating the function, any classes and constants that had been declared using the -/// [`macro@php_class`], [`macro@php_const`] and [`macro@php_impl`] attributes will be registered -/// inside this function. +/// As well as annotating the function, any classes and constants that had been +/// declared using the [`macro@php_class`], [`macro@php_const`] and +/// [`macro@php_impl`] attributes will be registered inside this function. /// -/// This function *must* be declared before the [`macro@php_module`] function, as this function -/// needs to be declared when building the module. +/// This function *must* be declared before the [`macro@php_module`] function, +/// as this function needs to be declared when building the module. /// -/// This function will automatically be generated if not already declared with this macro if you -/// have registered any classes or constants when using the [`macro@php_module`] macro. +/// This function will automatically be generated if not already declared with +/// this macro if you have registered any classes or constants when using the +/// [`macro@php_module`] macro. /// /// # Example /// @@ -478,19 +509,22 @@ pub use ext_php_rs_derive::php_class; /// ``` pub use ext_php_rs_derive::php_startup; -/// Derives the traits required to convert a struct or enum to and from a [`Zval`]. -/// Both [`FromZval`] and [`IntoZval`] are implemented on types which use this macro. +/// Derives the traits required to convert a struct or enum to and from a +/// [`Zval`]. Both [`FromZval`] and [`IntoZval`] are implemented on types which +/// use this macro. /// /// # Structs /// -/// When the macro is used on a struct, the [`FromZendObject`] and [`IntoZendObject`] traits are also -/// implemented, and will attempt to retrieve values for the struct fields from the objects properties. -/// This can be useful when you expect some arbitrary object (of which the type does not matter), but -/// you care about the value of the properties. +/// When the macro is used on a struct, the [`FromZendObject`] and +/// [`IntoZendObject`] traits are also implemented, and will attempt to retrieve +/// values for the struct fields from the objects properties. This can be useful +/// when you expect some arbitrary object (of which the type does not matter), +/// but you care about the value of the properties. /// -/// All properties must implement [`FromZval`] and [`IntoZval`] themselves. Generics are supported, -/// however, a [`FromZval`] and [`IntoZval`] bound will be added. If one property cannot be retrieved -/// from the object, the whole conversion will fail. +/// All properties must implement [`FromZval`] and [`IntoZval`] themselves. +/// Generics are supported, however, a [`FromZval`] and [`IntoZval`] bound will +/// be added. If one property cannot be retrieved from the object, the whole +/// conversion will fail. /// /// ## Examples /// @@ -550,19 +584,22 @@ pub use ext_php_rs_derive::php_startup; /// /// # Enums /// -/// When the macro is used on an enum, the [`FromZval`] and [`IntoZval`] implementations will treat -/// the enum as a tagged union with a mixed datatype. This allows you to accept two different types -/// in a parameter, for example, a string and an integer. +/// When the macro is used on an enum, the [`FromZval`] and [`IntoZval`] +/// implementations will treat the enum as a tagged union with a mixed datatype. +/// This allows you to accept two different types in a parameter, for example, a +/// string and an integer. /// -/// The enum variants must not have named fields (i.e. not in the form of a struct), and must have -/// exactly one field, the type to extract from the [`Zval`]. Optionally, the enum may have a -/// single default, empty variant, which is used when the [`Zval`] did not contain any data to fill +/// The enum variants must not have named fields (i.e. not in the form of a +/// struct), and must have exactly one field, the type to extract from the +/// [`Zval`]. Optionally, the enum may have a single default, empty variant, +/// which is used when the [`Zval`] did not contain any data to fill /// the other variants. This empty variant is equivalent to `null` in PHP. /// -/// The ordering of the enum variants is important, as the [`Zval`] contents is matched in order of -/// the variants. For example, [`Zval::string`] will attempt to read a string from the [`Zval`], -/// and if the [`Zval`] contains a long, the long will be converted to a string. If a string -/// variant was placed above an integer variant in the enum, the integer would be converted into a +/// The ordering of the enum variants is important, as the [`Zval`] contents is +/// matched in order of the variants. For example, [`Zval::string`] will attempt +/// to read a string from the [`Zval`], and if the [`Zval`] contains a long, the +/// long will be converted to a string. If a string variant was placed above an +/// integer variant in the enum, the integer would be converted into a /// string and passed as the string variant. /// /// ## Examples diff --git a/src/macros.rs b/src/macros.rs index e693e9c798..d372dd930e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,9 +1,9 @@ -//! Macros for interacting with PHP, mainly when the function takes variadic arguments. -//! Unforutunately, this is the best way to handle these. +//! Macros for interacting with PHP, mainly when the function takes variadic +//! arguments. Unforutunately, this is the best way to handle these. //! Note that most of these will introduce unsafe into your code base. -/// Starts the PHP extension information table displayed when running `phpinfo();` -/// Must be run *before* rows are inserted into the table. +/// Starts the PHP extension information table displayed when running +/// `phpinfo();` Must be run *before* rows are inserted into the table. #[macro_export] macro_rules! info_table_start { () => { @@ -11,7 +11,8 @@ macro_rules! info_table_start { }; } -/// Ends the PHP extension information table. Must be run *after* all rows have been inserted into the table. +/// Ends the PHP extension information table. Must be run *after* all rows have +/// been inserted into the table. #[macro_export] macro_rules! info_table_end { () => { @@ -19,19 +20,22 @@ macro_rules! info_table_end { }; } -/// Sets the header for the PHP extension information table. Takes as many string arguments as required. +/// Sets the header for the PHP extension information table. Takes as many +/// string arguments as required. #[macro_export] macro_rules! info_table_header { ($($element:expr),*) => {$crate::_info_table_row!(php_info_print_table_header, $($element),*)}; } -/// Adds a row to the PHP extension information table. Takes as many string arguments as required. +/// Adds a row to the PHP extension information table. Takes as many string +/// arguments as required. #[macro_export] macro_rules! info_table_row { ($($element:expr),*) => {$crate::_info_table_row!(php_info_print_table_row, $($element),*)}; } -/// INTERNAL: Calls a variadic C function with the number of parameters, then following with the parameters. +/// INTERNAL: Calls a variadic C function with the number of parameters, then +/// following with the parameters. #[doc(hidden)] #[macro_export] macro_rules! _info_table_row { @@ -51,9 +55,11 @@ macro_rules! _info_table_row { /// /// # Parameters /// -/// * `$fn` - The 'function' to call. Can be an [`Arg`](crate::php::args::Arg) or a +/// * `$fn` - The 'function' to call. Can be an [`Arg`](crate::php::args::Arg) +/// or a /// [`Zval`](crate::php::types::zval::Zval). -/// * ...`$param` - The parameters to pass to the function. Must be able to be converted into a +/// * ...`$param` - The parameters to pass to the function. Must be able to be +/// converted into a /// [`Zval`](crate::php::types::zval::Zval). #[macro_export] macro_rules! call_user_func { @@ -66,13 +72,14 @@ macro_rules! call_user_func { }; } -/// Parses a given list of arguments using the [`ArgParser`](crate::php::args::ArgParser) class. +/// Parses a given list of arguments using the +/// [`ArgParser`](crate::php::args::ArgParser) class. /// /// # Examples /// -/// This example parses all of the arguments. If one is invalid, execution of the function will -/// stop at the `parse_args!` macro invocation. The user is notified via PHP's argument parsing -/// system. +/// This example parses all of the arguments. If one is invalid, execution of +/// the function will stop at the `parse_args!` macro invocation. The user is +/// notified via PHP's argument parsing system. /// /// In this case, all of the arguments are required. /// @@ -95,8 +102,9 @@ macro_rules! call_user_func { /// } /// ``` /// -/// This example is similar to the one above, apart from the fact that the `z` argument is not -/// required. Note the semicolon seperating the first two arguments from the second. +/// This example is similar to the one above, apart from the fact that the `z` +/// argument is not required. Note the semicolon seperating the first two +/// arguments from the second. /// /// ``` /// use ext_php_rs::{ @@ -140,7 +148,8 @@ macro_rules! parse_args { /// Throws an exception and returns from the current function. /// -/// Wraps the [`throw`] function by inserting a `return` statement after throwing the exception. +/// Wraps the [`throw`] function by inserting a `return` statement after +/// throwing the exception. /// /// [`throw`]: crate::php::exceptions::throw /// @@ -170,9 +179,10 @@ macro_rules! throw { }; } -/// Implements a set of traits required to convert types that implement [`RegisteredClass`] to and -/// from [`ZendObject`]s and [`Zval`]s. Generally, this macro should not be called directly, as it -/// is called on any type that uses the [`#[php_class]`] macro. +/// Implements a set of traits required to convert types that implement +/// [`RegisteredClass`] to and from [`ZendObject`]s and [`Zval`]s. Generally, +/// this macro should not be called directly, as it is called on any type that +/// uses the [`#[php_class]`] macro. /// /// The following traits are implemented: /// @@ -183,7 +193,8 @@ macro_rules! throw { /// * `IntoZendObject for T` /// * `IntoZval for T` /// -/// These implementations are required while we wait on the stabilisation of specialisation. +/// These implementations are required while we wait on the stabilisation of +/// specialisation. /// /// # Examples /// diff --git a/src/props.rs b/src/props.rs index c30d03d61e..aaeef0aff6 100644 --- a/src/props.rs +++ b/src/props.rs @@ -9,9 +9,9 @@ use crate::{ /// Implemented on types which can be used as PHP properties. /// -/// Generally, this should not be directly implemented on types, as it is automatically implemented on -/// types that implement [`Clone`], [`IntoZval`] and [`FromZval`], which will be required to implement -/// this trait regardless. +/// Generally, this should not be directly implemented on types, as it is +/// automatically implemented on types that implement [`Clone`], [`IntoZval`] +/// and [`FromZval`], which will be required to implement this trait regardless. pub trait Prop<'a> { /// Gets the value of `self` by setting the value of `zv`. /// @@ -45,8 +45,8 @@ impl<'a, T: Clone + IntoZval + FromZval<'a>> Prop<'a> for T { /// There are two types of properties: /// /// * Field properties, where the data is stored inside a struct field. -/// * Method properties, where getter and/or setter functions are provided, which are used to get and set -/// the value of the property. +/// * Method properties, where getter and/or setter functions are provided, +/// which are used to get and set the value of the property. pub enum Property<'a, T> { Field(Box &mut dyn Prop>), Method { @@ -81,13 +81,15 @@ impl<'a, T: 'a> Property<'a, T> { /// Creates a method property with getters and setters. /// - /// If either the getter or setter is not given, an exception will be thrown when attempting to - /// retrieve/set the property. + /// If either the getter or setter is not given, an exception will be thrown + /// when attempting to retrieve/set the property. /// /// # Parameters /// - /// * `get` - Function used to get the value of the property, in an [`Option`]. - /// * `set` - Function used to set the value of the property, in an [`Option`]. + /// * `get` - Function used to get the value of the property, in an + /// [`Option`]. + /// * `set` - Function used to set the value of the property, in an + /// [`Option`]. /// /// # Examples /// @@ -133,10 +135,12 @@ impl<'a, T: 'a> Property<'a, T> { Self::Method { get, set } } - /// Attempts to retrieve the value of the property from the given object `self_`. + /// Attempts to retrieve the value of the property from the given object + /// `self_`. /// - /// The value of the property, if successfully retrieved, is loaded into the given [`Zval`] `retval`. If - /// unsuccessful, a [`PhpException`] is returned inside the error variant of a result. + /// The value of the property, if successfully retrieved, is loaded into the + /// given [`Zval`] `retval`. If unsuccessful, a [`PhpException`] is + /// returned inside the error variant of a result. /// /// # Parameters /// @@ -145,7 +149,8 @@ impl<'a, T: 'a> Property<'a, T> { /// /// # Returns /// - /// Nothing upon success, a [`PhpException`] inside an error variant when the property could not be retrieved. + /// Nothing upon success, a [`PhpException`] inside an error variant when + /// the property could not be retrieved. /// /// # Examples /// @@ -177,10 +182,12 @@ impl<'a, T: 'a> Property<'a, T> { } } - /// Attempts to set the value of the property inside the given object `self_`. + /// Attempts to set the value of the property inside the given object + /// `self_`. /// - /// The new value of the property is supplied inside the given [`Zval`] `value`. If unsuccessful, - /// a [`PhpException`] is returned inside the error variant of a result. + /// The new value of the property is supplied inside the given [`Zval`] + /// `value`. If unsuccessful, a [`PhpException`] is returned inside the + /// error variant of a result. /// /// # Parameters /// @@ -189,7 +196,8 @@ impl<'a, T: 'a> Property<'a, T> { /// /// # Returns /// - /// Nothing upon success, a [`PhpException`] inside an error variant when the property could not be set. + /// Nothing upon success, a [`PhpException`] inside an error variant when + /// the property could not be set. /// /// # Examples /// diff --git a/src/types/array.rs b/src/types/array.rs index 7271cdafd7..336ffa5ad8 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -1,5 +1,5 @@ -//! Represents an array in PHP. As all arrays in PHP are associative arrays, they are represented -//! by hash tables. +//! Represents an array in PHP. As all arrays in PHP are associative arrays, +//! they are represented by hash tables. use std::{ collections::HashMap, @@ -28,8 +28,8 @@ use crate::{ /// PHP array, which is represented in memory as a hashtable. pub type HashTable = crate::ffi::HashTable; -// Clippy complains about there being no `is_empty` function when implementing on the alias `ZendStr` :( -// +// Clippy complains about there being no `is_empty` function when implementing +// on the alias `ZendStr` :( #[allow(clippy::len_without_is_empty)] impl HashTable { /// Creates a new, empty, PHP associative array. @@ -76,7 +76,8 @@ impl HashTable { /// /// # Returns /// - /// * `Some(&Zval)` - A reference to the zval at the position in the hash table. + /// * `Some(&Zval)` - A reference to the zval at the position in the hash + /// table. /// * `None` - No value at the given position was found. pub fn get(&self, key: &'_ str) -> Option<&Zval> { let str = CString::new(key).ok()?; @@ -91,7 +92,8 @@ impl HashTable { /// /// # Returns /// - /// * `Some(&Zval)` - A reference to the zval at the position in the hash table. + /// * `Some(&Zval)` - A reference to the zval at the position in the hash + /// table. /// * `None` - No value at the given position was found. pub fn get_index(&self, key: u64) -> Option<&Zval> { unsafe { zend_hash_index_find(self, key).as_ref() } @@ -138,8 +140,8 @@ impl HashTable { } } - /// Attempts to insert an item into the hash table, or update if the key already exists. - /// Returns nothing in a result if successful. + /// Attempts to insert an item into the hash table, or update if the key + /// already exists. Returns nothing in a result if successful. /// /// # Parameters /// @@ -162,8 +164,8 @@ impl HashTable { Ok(()) } - /// Inserts an item into the hash table at a specified index, or updates if the key already exists. - /// Returns nothing in a result if successful. + /// Inserts an item into the hash table at a specified index, or updates if + /// the key already exists. Returns nothing in a result if successful. /// /// # Parameters /// @@ -179,8 +181,8 @@ impl HashTable { Ok(()) } - /// Pushes an item onto the end of the hash table. Returns a result containing nothing if the - /// element was sucessfully inserted. + /// Pushes an item onto the end of the hash table. Returns a result + /// containing nothing if the element was sucessfully inserted. /// /// # Parameters /// @@ -196,13 +198,15 @@ impl HashTable { Ok(()) } - /// Returns an iterator over the key(s) and value contained inside the hashtable. + /// Returns an iterator over the key(s) and value contained inside the + /// hashtable. #[inline] pub fn iter(&self) -> Iter { Iter::new(self) } - /// Returns an iterator over the values contained inside the hashtable, as if it was a set or list. + /// Returns an iterator over the values contained inside the hashtable, as + /// if it was a set or list. #[inline] pub fn values(&self) -> Values { Values::new(self) @@ -308,7 +312,8 @@ impl<'a> DoubleEndedIterator for Iter<'a> { } } -/// Immutable iterator which iterates over the values of the hashtable, as it was a set or list. +/// Immutable iterator which iterates over the values of the hashtable, as it +/// was a set or list. pub struct Values<'a>(Iter<'a>); impl<'a> Values<'a> { @@ -513,8 +518,8 @@ impl FromIterator for ZBox { fn from_iter>(iter: T) -> Self { let mut ht = HashTable::new(); for item in iter.into_iter() { - // Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval - // fails. + // Inserting a zval cannot fail, as `push` only returns `Err` if converting + // `val` to a zval fails. let _ = ht.push(item); } ht @@ -525,8 +530,8 @@ impl FromIterator<(u64, Zval)> for ZBox { fn from_iter>(iter: T) -> Self { let mut ht = HashTable::new(); for (key, val) in iter.into_iter() { - // Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval - // fails. + // Inserting a zval cannot fail, as `push` only returns `Err` if converting + // `val` to a zval fails. let _ = ht.insert_at_index(key, val); } ht @@ -537,8 +542,8 @@ impl<'a> FromIterator<(&'a str, Zval)> for ZBox { fn from_iter>(iter: T) -> Self { let mut ht = HashTable::new(); for (key, val) in iter.into_iter() { - // Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval - // fails. + // Inserting a zval cannot fail, as `push` only returns `Err` if converting + // `val` to a zval fails. let _ = ht.insert(key, val); } ht diff --git a/src/types/callable.rs b/src/types/callable.rs index cadeac5a6a..2b2ac35378 100644 --- a/src/types/callable.rs +++ b/src/types/callable.rs @@ -11,8 +11,9 @@ use crate::{ use super::Zval; -/// Acts as a wrapper around a callable [`Zval`]. Allows the owner to call the [`Zval`] as if it -/// was a PHP function through the [`try_call`](Callable::try_call) method. +/// Acts as a wrapper around a callable [`Zval`]. Allows the owner to call the +/// [`Zval`] as if it was a PHP function through the +/// [`try_call`](Callable::try_call) method. #[derive(Debug)] pub struct Callable<'a>(OwnedZval<'a>); @@ -34,8 +35,8 @@ impl<'a> Callable<'a> { } } - /// Attempts to create a new [`Callable`] by taking ownership of a Zval. Returns a result - /// containing the callable if the zval was callable. + /// Attempts to create a new [`Callable`] by taking ownership of a Zval. + /// Returns a result containing the callable if the zval was callable. /// /// # Parameters /// @@ -48,8 +49,9 @@ impl<'a> Callable<'a> { } } - /// Attempts to create a new [`Callable`] from a function name. Returns a result containing the - /// callable if the function existed and was callable. + /// Attempts to create a new [`Callable`] from a function name. Returns a + /// result containing the callable if the function existed and was + /// callable. /// /// # Parameters /// @@ -61,12 +63,14 @@ impl<'a> Callable<'a> { Self::new_owned(callable) } - /// Attempts to call the callable with a list of arguments to pass to the function. - /// Note that a thrown exception inside the callable is not detectable, therefore you should - /// check if the return value is valid rather than unwrapping. Returns a result containing the - /// return value of the function, or an error. + /// Attempts to call the callable with a list of arguments to pass to the + /// function. Note that a thrown exception inside the callable is not + /// detectable, therefore you should check if the return value is valid + /// rather than unwrapping. Returns a result containing the return value + /// of the function, or an error. /// - /// You should not call this function directly, rather through the [`call_user_func`] macro. + /// You should not call this function directly, rather through the + /// [`call_user_func`] macro. /// /// # Parameters /// @@ -119,7 +123,8 @@ impl<'a> TryFrom for Callable<'a> { } } -/// A container for a zval. Either contains a reference to a zval or an owned zval. +/// A container for a zval. Either contains a reference to a zval or an owned +/// zval. #[derive(Debug)] enum OwnedZval<'a> { Reference(&'a Zval), diff --git a/src/types/class_object.rs b/src/types/class_object.rs index 6316d52a37..33e41767fe 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -1,5 +1,5 @@ -//! Represents an object in PHP. Allows for overriding the internal object used by classes, -//! allowing users to store Rust data inside a PHP object. +//! Represents an object in PHP. Allows for overriding the internal object used +//! by classes, allowing users to store Rust data inside a PHP object. use std::{ fmt::Debug, @@ -29,8 +29,9 @@ pub struct ZendClassObject { } impl ZendClassObject { - /// Creates a new [`ZendClassObject`] of type `T`, where `T` is a [`RegisteredClass`] in PHP, storing the - /// given value `val` inside the object. + /// Creates a new [`ZendClassObject`] of type `T`, where `T` is a + /// [`RegisteredClass`] in PHP, storing the given value `val` inside the + /// object. /// /// # Parameters /// @@ -43,19 +44,21 @@ impl ZendClassObject { unsafe { Self::internal_new(Some(val)) } } - /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized internal object. + /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized + /// internal object. /// /// # Safety /// - /// As the object is uninitialized, the caller must ensure the following until the internal object is - /// initialized: + /// As the object is uninitialized, the caller must ensure the following + /// until the internal object is initialized: /// /// * The object is never dereferenced to `T`. /// * The [`Clone`] implementation is never called. /// * The [`Debug`] implementation is never called. /// - /// If any of these conditions are not met while not initialized, the corresponding function will panic. - /// Converting the object into its inner pointer with the [`into_raw`] function is valid, however. + /// If any of these conditions are not met while not initialized, the + /// corresponding function will panic. Converting the object into its + /// inner pointer with the [`into_raw`] function is valid, however. /// /// [`into_raw`]: #method.into_raw /// @@ -66,8 +69,8 @@ impl ZendClassObject { Self::internal_new(None) } - /// Creates a new [`ZendObject`] of type `T`, storing the given (and potentially uninitialized) `val` - /// inside the object. + /// Creates a new [`ZendObject`] of type `T`, storing the given (and + /// potentially uninitialized) `val` inside the object. /// /// # Parameters /// @@ -78,16 +81,18 @@ impl ZendClassObject { /// /// Providing an initialized variant of [`MaybeUninit`] is safe. /// - /// Providing an uninitalized variant of [`MaybeUninit`] is unsafe. As the object is uninitialized, - /// the caller must ensure the following until the internal object is initialized: + /// Providing an uninitalized variant of [`MaybeUninit`] is unsafe. As + /// the object is uninitialized, the caller must ensure the following + /// until the internal object is initialized: /// /// * The object is never dereferenced to `T`. /// * The [`Clone`] implementation is never called. /// * The [`Debug`] implementation is never called. /// - /// If any of these conditions are not met while not initialized, the corresponding function will panic. - /// Converting the object into its inner with the [`into_raw`] function is valid, however. You can initialize - /// the object with the [`initialize`] function. + /// If any of these conditions are not met while not initialized, the + /// corresponding function will panic. Converting the object into its + /// inner with the [`into_raw`] function is valid, however. You can + /// initialize the object with the [`initialize`] function. /// /// [`into_raw`]: #method.into_raw /// [`initialize`]: #method.initialize @@ -124,14 +129,14 @@ impl ZendClassObject { /// /// # Returns /// - /// Returns the old value in an [`Option`] if the object had already been initialized, [`None`] - /// otherwise. + /// Returns the old value in an [`Option`] if the object had already been + /// initialized, [`None`] otherwise. pub fn initialize(&mut self, val: T) -> Option { self.obj.replace(val) } - /// Returns a reference to the [`ZendClassObject`] of a given object `T`. Returns [`None`] - /// if the given object is not of the type `T`. + /// Returns a reference to the [`ZendClassObject`] of a given object `T`. + /// Returns [`None`] if the given object is not of the type `T`. /// /// # Parameters /// @@ -139,8 +144,8 @@ impl ZendClassObject { /// /// # Safety /// - /// Caller must guarantee that the given `obj` was created by Zend, which means that it - /// is immediately followed by a [`zend_object`]. + /// Caller must guarantee that the given `obj` was created by Zend, which + /// means that it is immediately followed by a [`zend_object`]. pub(crate) unsafe fn from_obj_ptr(obj: &T) -> Option<&mut Self> { // TODO(david): Remove this function let ptr = (obj as *const T as *mut Self).as_mut()?; @@ -152,8 +157,9 @@ impl ZendClassObject { } } - /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. - /// Returns [`None`] if the given object is not of the type `T`. + /// Returns a mutable reference to the [`ZendClassObject`] of a given zend + /// object `obj`. Returns [`None`] if the given object is not of the + /// type `T`. /// /// # Parameters /// @@ -162,8 +168,9 @@ impl ZendClassObject { Some(Self::_from_zend_obj(std)?) } - /// Returns a mutable reference to the [`ZendClassObject`] of a given zend object `obj`. - /// Returns [`None`] if the given object is not of the type `T`. + /// Returns a mutable reference to the [`ZendClassObject`] of a given zend + /// object `obj`. Returns [`None`] if the given object is not of the + /// type `T`. /// /// # Parameters /// @@ -234,8 +241,9 @@ impl<'a, T: RegisteredClass> FromZendObjectMut<'a> for &'a mut ZendClassObject ZBoxable for ZendClassObject { fn free(&mut self) { - // SAFETY: All constructors guarantee that `self` contains a valid pointer. Further, all constructors - // guarantee that the `std` field of `ZendClassObject` will be initialized. + // SAFETY: All constructors guarantee that `self` contains a valid pointer. + // Further, all constructors guarantee that the `std` field of + // `ZendClassObject` will be initialized. unsafe { ext_php_rs_zend_object_release(&mut self.std) } } } @@ -267,9 +275,10 @@ impl Default for ZBox> { impl Clone for ZBox> { fn clone(&self) -> Self { - // SAFETY: All constructors of `NewClassObject` guarantee that it will contain a valid pointer. - // The constructor also guarantees that the internal `ZendClassObject` pointer will contain a valid, - // initialized `obj`, therefore we can dereference both safely. + // SAFETY: All constructors of `NewClassObject` guarantee that it will contain a + // valid pointer. The constructor also guarantees that the internal + // `ZendClassObject` pointer will contain a valid, initialized `obj`, + // therefore we can dereference both safely. unsafe { let mut new = ZendClassObject::new((&***self).clone()); zend_objects_clone_members(&mut new.std, &self.std as *const _ as *mut _); diff --git a/src/types/long.rs b/src/types/long.rs index 5f9389506d..806663bbbf 100644 --- a/src/types/long.rs +++ b/src/types/long.rs @@ -1,5 +1,6 @@ -//! Represents an integer introduced in PHP. Note that the size of this integer differs. -//! On a 32-bit system, a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. +//! Represents an integer introduced in PHP. Note that the size of this integer +//! differs. On a 32-bit system, a ZendLong is 32-bits, while on a 64-bit system +//! it is 64-bits. use crate::{ convert::IntoZval, diff --git a/src/types/object.rs b/src/types/object.rs index 3490c6d59e..4272d682c4 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -1,5 +1,5 @@ -//! Represents an object in PHP. Allows for overriding the internal object used by classes, -//! allowing users to store Rust data inside a PHP object. +//! Represents an object in PHP. Allows for overriding the internal object used +//! by classes, allowing users to store Rust data inside a PHP object. use std::{convert::TryInto, fmt::Debug, ops::DerefMut}; @@ -22,7 +22,8 @@ use crate::{ pub type ZendObject = zend_object; impl ZendObject { - /// Creates a new [`ZendObject`], returned inside an [`ZBox`] wrapper. + /// Creates a new [`ZendObject`], returned inside an [`ZBox`] + /// wrapper. /// /// # Parameters /// @@ -32,8 +33,8 @@ impl ZendObject { /// /// Panics when allocating memory for the new object fails. pub fn new(ce: &ClassEntry) -> ZBox { - // SAFETY: Using emalloc to allocate memory inside Zend arena. Casting `ce` to `*mut` is valid - // as the function will not mutate `ce`. + // SAFETY: Using emalloc to allocate memory inside Zend arena. Casting `ce` to + // `*mut` is valid as the function will not mutate `ce`. unsafe { let ptr = zend_objects_new(ce as *const _ as *mut _); ZBox::from_raw( @@ -43,15 +44,16 @@ impl ZendObject { } } - /// Creates a new `stdClass` instance, returned inside an [`ZBox`] wrapper. + /// Creates a new `stdClass` instance, returned inside an + /// [`ZBox`] wrapper. /// /// # Panics /// - /// Panics if allocating memory for the object fails, or if the `stdClass` class entry has not been - /// registered with PHP yet. + /// Panics if allocating memory for the object fails, or if the `stdClass` + /// class entry has not been registered with PHP yet. pub fn new_stdclass() -> ZBox { - // SAFETY: This will be `NULL` until it is initialized. `as_ref()` checks for null, - // so we can panic if it's null. + // SAFETY: This will be `NULL` until it is initialized. `as_ref()` checks for + // null, so we can panic if it's null. Self::new(unsafe { zend_standard_class_def .as_ref() @@ -59,8 +61,8 @@ impl ZendObject { }) } - /// Converts the class object into an owned [`ZendObject`]. This removes any possibility of - /// accessing the underlying attached Rust struct. + /// Converts the class object into an owned [`ZendObject`]. This removes any + /// possibility of accessing the underlying attached Rust struct. pub fn from_class_object(obj: ZBox>) -> ZBox { let this = obj.into_raw(); // SAFETY: Consumed box must produce a well-aligned non-null pointer. @@ -78,14 +80,15 @@ impl ZendObject { } } - /// Checks if the given object is an instance of a registered class with Rust - /// type `T`. + /// Checks if the given object is an instance of a registered class with + /// Rust type `T`. pub fn is_instance(&self) -> bool { (self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _)) } - /// Attempts to read a property from the Object. Returns a result containing the - /// value of the property if it exists and can be read, and an [`Error`] otherwise. + /// Attempts to read a property from the Object. Returns a result containing + /// the value of the property if it exists and can be read, and an + /// [`Error`] otherwise. /// /// # Parameters /// @@ -140,9 +143,9 @@ impl ZendObject { Ok(()) } - /// Checks if a property exists on an object. Takes a property name and query parameter, - /// which defines what classifies if a property exists or not. See [`PropertyQuery`] for - /// more information. + /// Checks if a property exists on an object. Takes a property name and + /// query parameter, which defines what classifies if a property exists + /// or not. See [`PropertyQuery`] for more information. /// /// # Parameters /// @@ -161,7 +164,8 @@ impl ZendObject { } > 0) } - /// Attempts to retrieve the properties of the object. Returned inside a Zend Hashtable. + /// Attempts to retrieve the properties of the object. Returned inside a + /// Zend Hashtable. pub fn get_properties(&self) -> Result<&HashTable> { unsafe { self.handlers()? @@ -177,9 +181,9 @@ impl ZendObject { self.handlers.as_ref().ok_or(Error::InvalidScope) } - /// Returns a mutable pointer to `self`, regardless of the type of reference. - /// Only to be used in situations where a C function requires a mutable pointer - /// but does not modify the underlying data. + /// Returns a mutable pointer to `self`, regardless of the type of + /// reference. Only to be used in situations where a C function requires + /// a mutable pointer but does not modify the underlying data. #[inline] fn mut_ptr(&self) -> *mut Self { (self as *const Self) as *mut Self @@ -232,8 +236,8 @@ impl IntoZval for ZBox { const TYPE: DataType = DataType::Object(None); fn set_zval(mut self, zv: &mut Zval, _: bool) -> Result<()> { - // We must decrement the refcounter on the object before inserting into the zval, - // as the reference counter will be incremented on add. + // We must decrement the refcounter on the object before inserting into the + // zval, as the reference counter will be incremented on add. // NOTE(david): again is this needed, we increment in `set_object`. self.dec_count(); zv.set_object(self.into_raw()); diff --git a/src/types/string.rs b/src/types/string.rs index ab37b35530..af67bcb087 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -1,5 +1,6 @@ -//! Represents a string in the PHP world. Similar to a C string, but is reference counted and -//! contains the length of the string, meaning the string can contain the NUL character. +//! Represents a string in the PHP world. Similar to a C string, but is +//! reference counted and contains the length of the string, meaning the string +//! can contain the NUL character. use std::{ borrow::Cow, @@ -29,21 +30,24 @@ use crate::{ /// A borrowed Zend-string. /// -/// Although this object does implement [`Sized`], it is in fact not sized. As C cannot represent unsized -/// types, an array of size 1 is used at the end of the type to represent the contents of the string, therefore -/// this type is actually unsized. All constructors return [`ZBox`], the owned varaint. +/// Although this object does implement [`Sized`], it is in fact not sized. As C +/// cannot represent unsized types, an array of size 1 is used at the end of the +/// type to represent the contents of the string, therefore this type is +/// actually unsized. All constructors return [`ZBox`], the owned +/// varaint. /// -/// Once the `ptr_metadata` feature lands in stable rust, this type can potentially be changed to a DST using -/// slices and metadata. See the tracking issue here: +/// Once the `ptr_metadata` feature lands in stable rust, this type can +/// potentially be changed to a DST using slices and metadata. See the tracking issue here: pub type ZendStr = zend_string; -// Adding to the Zend interned string hashtable is not atomic and can be contested when PHP is compiled with ZTS, -// so an empty mutex is used to ensure no collisions occur on the Rust side. Not much we can do about collisions +// Adding to the Zend interned string hashtable is not atomic and can be +// contested when PHP is compiled with ZTS, so an empty mutex is used to ensure +// no collisions occur on the Rust side. Not much we can do about collisions // on the PHP side. static INTERNED_LOCK: Mutex = Mutex::const_new(RawMutex::INIT, ()); -// Clippy complains about there being no `is_empty` function when implementing on the alias `ZendStr` :( -// +// Clippy complains about there being no `is_empty` function when implementing +// on the alias `ZendStr` :( #[allow(clippy::len_without_is_empty)] impl ZendStr { /// Creates a new Zend string from a [`str`]. @@ -51,16 +55,19 @@ impl ZendStr { /// # Parameters /// /// * `str` - String content. - /// * `persistent` - Whether the string should persist through the request boundary. + /// * `persistent` - Whether the string should persist through the request + /// boundary. /// /// # Returns /// - /// Returns a result containing the Zend string if successful. Returns an error if the given - /// string contains NUL bytes, which cannot be contained inside a C string. + /// Returns a result containing the Zend string if successful. Returns an + /// error if the given string contains NUL bytes, which cannot be + /// contained inside a C string. /// /// # Panics /// - /// Panics if the function was unable to allocate memory for the Zend string. + /// Panics if the function was unable to allocate memory for the Zend + /// string. pub fn new(str: &str, persistent: bool) -> Result> { Ok(Self::from_c_str(&CString::new(str)?, persistent)) } @@ -70,11 +77,13 @@ impl ZendStr { /// # Parameters /// /// * `str` - String content. - /// * `persistent` - Whether the string should persist through the request boundary. + /// * `persistent` - Whether the string should persist through the request + /// boundary. /// /// # Panics /// - /// Panics if the function was unable to allocate memory for the Zend string. + /// Panics if the function was unable to allocate memory for the Zend + /// string. pub fn from_c_str(str: &CStr, persistent: bool) -> ZBox { unsafe { let ptr = @@ -89,41 +98,47 @@ impl ZendStr { /// Creates a new interned Zend string from a [`str`]. /// - /// An interned string is only ever stored once and is immutable. PHP stores the string in an - /// internal hashtable which stores the interned strings. + /// An interned string is only ever stored once and is immutable. PHP stores + /// the string in an internal hashtable which stores the interned + /// strings. /// - /// As Zend hashtables are not thread-safe, a mutex is used to prevent two interned strings from - /// being created at the same time. + /// As Zend hashtables are not thread-safe, a mutex is used to prevent two + /// interned strings from being created at the same time. /// /// # Parameters /// /// * `str` - String content. - /// * `persistent` - Whether the string should persist through the request boundary. + /// * `persistent` - Whether the string should persist through the request + /// boundary. /// /// # Returns /// - /// Returns a result containing the Zend string if successful. Returns an error if the given - /// string contains NUL bytes, which cannot be contained inside a C string. + /// Returns a result containing the Zend string if successful. Returns an + /// error if the given string contains NUL bytes, which cannot be + /// contained inside a C string. /// /// # Panics /// - /// Panics if the function was unable to allocate memory for the Zend string. + /// Panics if the function was unable to allocate memory for the Zend + /// string. pub fn new_interned(str: &str, persistent: bool) -> Result> { Ok(Self::interned_from_c_str(&CString::new(str)?, persistent)) } /// Creates a new interned Zend string from a [`CStr`]. /// - /// An interned string is only ever stored once and is immutable. PHP stores the string in an - /// internal hashtable which stores the interned strings. + /// An interned string is only ever stored once and is immutable. PHP stores + /// the string in an internal hashtable which stores the interned + /// strings. /// - /// As Zend hashtables are not thread-safe, a mutex is used to prevent two interned strings from - /// being created at the same time. + /// As Zend hashtables are not thread-safe, a mutex is used to prevent two + /// interned strings from being created at the same time. /// /// # Parameters /// /// * `str` - String content. - /// * `persistent` - Whether the string should persist through the request boundary. + /// * `persistent` - Whether the string should persist through the request + /// boundary. /// /// # Panics /// @@ -167,9 +182,11 @@ impl ZendStr { } } - /// Attempts to return a reference to the underlying [`str`] inside the Zend string. + /// Attempts to return a reference to the underlying [`str`] inside the Zend + /// string. /// - /// Returns the [`None`] variant if the [`CStr`] contains non-UTF-8 characters. + /// Returns the [`None`] variant if the [`CStr`] contains non-UTF-8 + /// characters. pub fn as_str(&self) -> Option<&str> { self.as_c_str().to_str().ok() } diff --git a/src/types/zval.rs b/src/types/zval.rs index a7ca614c2b..a92b7c0cdb 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -1,5 +1,6 @@ -//! The base value in PHP. A Zval can contain any PHP type, and the type that it contains is -//! determined by a property inside the struct. The content of the Zval is stored in a union. +//! The base value in PHP. A Zval can contain any PHP type, and the type that it +//! contains is determined by a property inside the struct. The content of the +//! Zval is stored in a union. use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr}; @@ -69,8 +70,9 @@ impl Zval { /// Returns the value of the zval as a zend string, if it is a string. /// - /// Note that this functions output will not be the same as [`string()`](#method.string), as - /// this function does not attempt to convert other types into a [`String`]. + /// Note that this functions output will not be the same as + /// [`string()`](#method.string), as this function does not attempt to + /// convert other types into a [`String`]. pub fn zend_str(&self) -> Option<&ZendStr> { if self.is_string() { unsafe { self.value.str_.as_ref() } @@ -81,10 +83,11 @@ impl Zval { /// Returns the value of the zval if it is a string. /// - /// If the zval does not contain a string, the function will check if it contains a - /// double or a long, and if so it will convert the value to a [`String`] and return it. - /// Don't rely on this logic, as there is potential for this to change to match the output - /// of the [`str()`](#method.str) function. + /// If the zval does not contain a string, the function will check if it + /// contains a double or a long, and if so it will convert the value to + /// a [`String`] and return it. Don't rely on this logic, as there is + /// potential for this to change to match the output of the [`str()`](# + /// method.str) function. pub fn string(&self) -> Option { self.str() .map(|s| s.to_string()) @@ -93,22 +96,24 @@ impl Zval { /// Returns the value of the zval if it is a string. /// - /// Note that this functions output will not be the same as [`string()`](#method.string), as - /// this function does not attempt to convert other types into a [`String`], as it could not - /// pass back a [`&str`] in those cases. + /// Note that this functions output will not be the same as + /// [`string()`](#method.string), as this function does not attempt to + /// convert other types into a [`String`], as it could not pass back a + /// [`&str`] in those cases. pub fn str(&self) -> Option<&str> { self.zend_str().and_then(|zs| zs.as_str()) } - /// Returns the value of the zval if it is a string and can be unpacked into a vector of a - /// given type. Similar to the [`unpack`](https://www.php.net/manual/en/function.unpack.php) + /// Returns the value of the zval if it is a string and can be unpacked into + /// a vector of a given type. Similar to the [`unpack`](https://www.php.net/manual/en/function.unpack.php) /// in PHP, except you can only unpack one type. /// /// # Safety /// - /// There is no way to tell if the data stored in the string is actually of the given type. - /// The results of this function can also differ from platform-to-platform due to the different - /// representation of some types on different platforms. Consult the [`pack`] function + /// There is no way to tell if the data stored in the string is actually of + /// the given type. The results of this function can also differ from + /// platform-to-platform due to the different representation of some + /// types on different platforms. Consult the [`pack`] function /// documentation for more details. /// /// [`pack`]: https://www.php.net/manual/en/function.pack.php @@ -132,7 +137,8 @@ impl Zval { } } - /// Returns an immutable reference to the underlying zval hashtable if the zval contains an array. + /// Returns an immutable reference to the underlying zval hashtable if the + /// zval contains an array. pub fn array(&self) -> Option<&HashTable> { if self.is_array() { unsafe { self.value.arr.as_ref() } @@ -141,7 +147,8 @@ impl Zval { } } - /// Returns a mutable reference to the underlying zval hashtable if the zval contains an array. + /// Returns a mutable reference to the underlying zval hashtable if the zval + /// contains an array. pub fn array_mut(&mut self) -> Option<&mut HashTable> { if self.is_array() { unsafe { self.value.arr.as_mut() } @@ -159,7 +166,8 @@ impl Zval { } } - /// Returns a mutable reference to the object contained in the [`Zval`], if any. + /// Returns a mutable reference to the object contained in the [`Zval`], if + /// any. pub fn object_mut(&mut self) -> Option<&mut ZendObject> { if self.is_object() { unsafe { self.value.obj.as_mut() } @@ -196,8 +204,9 @@ impl Zval { /// /// # Safety /// - /// The caller must ensure that the pointer contained in the zval is in fact a pointer to an - /// instance of `T`, as the zval has no way of defining the type of pointer. + /// The caller must ensure that the pointer contained in the zval is in fact + /// a pointer to an instance of `T`, as the zval has no way of defining + /// the type of pointer. pub unsafe fn ptr(&self) -> Option<*mut T> { if self.is_ptr() { Some(self.value.ptr as *mut T) @@ -206,12 +215,14 @@ impl Zval { } } - /// Attempts to call the zval as a callable with a list of arguments to pass to the function. - /// Note that a thrown exception inside the callable is not detectable, therefore you should - /// check if the return value is valid rather than unwrapping. Returns a result containing the - /// return value of the function, or an error. + /// Attempts to call the zval as a callable with a list of arguments to pass + /// to the function. Note that a thrown exception inside the callable is + /// not detectable, therefore you should check if the return value is + /// valid rather than unwrapping. Returns a result containing the return + /// value of the function, or an error. /// - /// You should not call this function directly, rather through the [`call_user_func`] macro. + /// You should not call this function directly, rather through the + /// [`call_user_func`] macro. /// /// # Parameters /// @@ -291,7 +302,8 @@ impl Zval { self.get_type() == DataType::Ptr } - /// Sets the value of the zval as a string. Returns nothing in a result when successful. + /// Sets the value of the zval as a string. Returns nothing in a result when + /// successful. /// /// # Parameters /// @@ -312,7 +324,8 @@ impl Zval { self.value.str_ = val.into_raw(); } - /// Sets the value of the zval as a binary string, which is represented in Rust as a vector. + /// Sets the value of the zval as a binary string, which is represented in + /// Rust as a vector. /// /// # Parameters /// @@ -323,7 +336,8 @@ impl Zval { self.value.str_ = ptr; } - /// Sets the value of the zval as a interned string. Returns nothing in a result when successful. + /// Sets the value of the zval as a interned string. Returns nothing in a + /// result when successful. /// /// # Parameters /// @@ -407,7 +421,8 @@ impl Zval { self.value.obj = (val as *const ZendObject) as *mut ZendObject; } - /// Sets the value of the zval as an array. Returns nothing in a result on success. + /// Sets the value of the zval as an array. Returns nothing in a result on + /// success. /// /// # Parameters /// @@ -417,7 +432,8 @@ impl Zval { Ok(()) } - /// Sets the value of the zval as an array. Returns nothing in a result on success. + /// Sets the value of the zval as an array. Returns nothing in a result on + /// success. /// /// # Parameters /// @@ -439,21 +455,24 @@ impl Zval { /// Used to drop the Zval but keep the value of the zval intact. /// - /// This is important when copying the value of the zval, as the actual value - /// will not be copied, but the pointer to the value (string for example) will be - /// copied. + /// This is important when copying the value of the zval, as the actual + /// value will not be copied, but the pointer to the value (string for + /// example) will be copied. pub(crate) fn release(mut self) { - // NOTE(david): don't use `change_type` here as we are wanting to keep the contents intact. + // NOTE(david): don't use `change_type` here as we are wanting to keep the + // contents intact. self.u1.type_info = ZvalTypeFlags::Null.bits(); } - /// Changes the type of the zval, freeing the current contents when applicable. + /// Changes the type of the zval, freeing the current contents when + /// applicable. /// /// # Parameters /// /// * `ty` - The new type of the zval. fn change_type(&mut self, ty: ZvalTypeFlags) { - // SAFETY: we have exclusive mutable access to this zval so can free the contents. + // SAFETY: we have exclusive mutable access to this zval so can free the + // contents. unsafe { zval_ptr_dtor(self) }; self.u1.type_info = ty.bits(); } diff --git a/src/zend/_type.rs b/src/zend/_type.rs index 7ca2d2bd12..4342e3fcf4 100644 --- a/src/zend/_type.rs +++ b/src/zend/_type.rs @@ -28,17 +28,20 @@ impl ZendType { } } - /// Attempts to create a zend type for a given datatype. Returns an option containing the type. + /// Attempts to create a zend type for a given datatype. Returns an option + /// containing the type. /// - /// Returns [`None`] if the data type was a class object where the class name could not be converted - /// into a C string (i.e. contained NUL-bytes). + /// Returns [`None`] if the data type was a class object where the class + /// name could not be converted into a C string (i.e. contained + /// NUL-bytes). /// /// # Parameters /// /// * `type_` - Data type to create zend type for. /// * `pass_by_ref` - Whether the type should be passed by reference. /// * `is_variadic` - Whether the type is for a variadic argument. - /// * `allow_null` - Whether the type should allow null to be passed in place. + /// * `allow_null` - Whether the type should allow null to be passed in + /// place. pub fn empty_from_type( type_: DataType, pass_by_ref: bool, @@ -58,17 +61,20 @@ impl ZendType { } } - /// Attempts to create a zend type for a class object type. Returns an option containing the type if successful. + /// Attempts to create a zend type for a class object type. Returns an + /// option containing the type if successful. /// - /// Returns [`None`] if the data type was a class object where the class name could not be converted - /// into a C string (i.e. contained NUL-bytes). + /// Returns [`None`] if the data type was a class object where the class + /// name could not be converted into a C string (i.e. contained + /// NUL-bytes). /// /// # Parameters /// /// * `class_name` - Name of the class parameter. /// * `pass_by_ref` - Whether the type should be passed by reference. /// * `is_variadic` - Whether the type is for a variadic argument. - /// * `allow_null` - Whether the type should allow null to be passed in place. + /// * `allow_null` - Whether the type should allow null to be passed in + /// place. fn empty_from_class_type( class_name: &str, pass_by_ref: bool, @@ -94,7 +100,8 @@ impl ZendType { /// * `type_` - Data type to create zend type for. /// * `pass_by_ref` - Whether the type should be passed by reference. /// * `is_variadic` - Whether the type is for a variadic argument. - /// * `allow_null` - Whether the type should allow null to be passed in place. + /// * `allow_null` - Whether the type should allow null to be passed in + /// place. /// /// # Panics /// diff --git a/src/zend/class.rs b/src/zend/class.rs index 909398addc..13c13d1f74 100644 --- a/src/zend/class.rs +++ b/src/zend/class.rs @@ -15,8 +15,8 @@ impl PartialEq for ClassEntry { impl ClassEntry { /// Attempts to find a reference to a class in the global class table. /// - /// Returns a reference to the class if found, or [`None`] if the class could - /// not be found or the class table has not been initialized. + /// Returns a reference to the class if found, or [`None`] if the class + /// could not be found or the class table has not been initialized. pub fn try_find(name: &str) -> Option<&'static Self> { ExecutorGlobals::get().class_table()?; let mut name = ZendStr::new(name, false).ok()?; @@ -31,7 +31,8 @@ impl ClassEntry { ClassFlags::from_bits_truncate(self.ce_flags) } - /// Returns `true` if the class entry is an interface, and `false` otherwise. + /// Returns `true` if the class entry is an interface, and `false` + /// otherwise. pub fn is_interface(&self) -> bool { self.flags().contains(ClassFlags::Interface) } @@ -73,8 +74,9 @@ impl ClassEntry { false } - /// Returns an iterator of all the interfaces that the class implements. Returns [`None`] if - /// the interfaces have not been resolved on the class. + /// Returns an iterator of all the interfaces that the class implements. + /// Returns [`None`] if the interfaces have not been resolved on the + /// class. pub fn interfaces(&self) -> Option> { self.flags() .contains(ClassFlags::ResolvedInterfaces) @@ -88,9 +90,9 @@ impl ClassEntry { /// Returns the parent of the class. /// - /// If the parent of the class has not been resolved, it attempts to find the parent by name. - /// Returns [`None`] if the parent was not resolved and the parent was not able to be found - /// by name. + /// If the parent of the class has not been resolved, it attempts to find + /// the parent by name. Returns [`None`] if the parent was not resolved + /// and the parent was not able to be found by name. pub fn parent(&self) -> Option<&Self> { if self.flags().contains(ClassFlags::ResolvedParent) { unsafe { self.__bindgen_anon_1.parent.as_ref() } diff --git a/src/zend/ex.rs b/src/zend/ex.rs index 0f569c8d73..ace8c9ece3 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -13,22 +13,25 @@ use crate::{ pub type ExecutionData = zend_execute_data; impl ExecutionData { - /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`. + /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside + /// `self`. pub fn parser<'a>(&'a mut self) -> ArgParser<'a, '_> { self.parser_object().0 } - /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`. + /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside + /// `self`. /// - /// A reference to `$this` is also returned in an [`Option`], which resolves to [`None`] - /// if this function is not called inside a method. + /// A reference to `$this` is also returned in an [`Option`], which resolves + /// to [`None`] if this function is not called inside a method. pub fn parser_object<'a>(&'a mut self) -> (ArgParser<'a, '_>, Option<&'a mut ZendObject>) { // SAFETY: All fields of the `u2` union are the same type. let n_args = unsafe { self.This.u2.num_args }; let mut args = vec![]; for i in 0..n_args { - // SAFETY: Function definition ensures arg lifetime doesn't exceed execution data lifetime. + // SAFETY: Function definition ensures arg lifetime doesn't exceed execution + // data lifetime. let arg = unsafe { self.zend_call_arg(i as usize) }; args.push(arg); } @@ -38,14 +41,16 @@ impl ExecutionData { (ArgParser::new(args), obj) } - /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`. + /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside + /// `self`. /// - /// A reference to `$this` is also returned in an [`Option`], which resolves to [`None`] - /// if this function is not called inside a method. + /// A reference to `$this` is also returned in an [`Option`], which resolves + /// to [`None`] if this function is not called inside a method. /// - /// This function differs from [`parse_object`] in the fact that it returns a reference to - /// a [`ZendClassObject`], which is an object that contains an arbitrary Rust type at the - /// start of the object. The object will also resolve to [`None`] if the function is called + /// This function differs from [`parse_object`] in the fact that it returns + /// a reference to a [`ZendClassObject`], which is an object that + /// contains an arbitrary Rust type at the start of the object. The + /// object will also resolve to [`None`] if the function is called /// inside a method that does not belong to an object with type `T`. /// /// [`parse_object`]: #method.parse_object @@ -59,29 +64,33 @@ impl ExecutionData { ) } - /// Attempts to retrieve a reference to the underlying class object of the Zend object. + /// Attempts to retrieve a reference to the underlying class object of the + /// Zend object. /// - /// Returns a [`ZendClassObject`] if the execution data contained a valid object of type `T`, - /// otherwise returns [`None`]. + /// Returns a [`ZendClassObject`] if the execution data contained a valid + /// object of type `T`, otherwise returns [`None`]. pub fn get_object(&mut self) -> Option<&mut ZendClassObject> { - // TODO(david): This should be a `&mut self` function but we need to fix arg parser first. + // TODO(david): This should be a `&mut self` function but we need to fix arg + // parser first. ZendClassObject::from_zend_obj_mut(self.get_self()?) } - /// Attempts to retrieve the 'this' object, which can be used in class methods - /// to retrieve the underlying Zend object. + /// Attempts to retrieve the 'this' object, which can be used in class + /// methods to retrieve the underlying Zend object. pub fn get_self(&mut self) -> Option<&mut ZendObject> { - // TODO(david): This should be a `&mut self` function but we need to fix arg parser first. + // TODO(david): This should be a `&mut self` function but we need to fix arg + // parser first. self.This.object_mut() } /// Translation of macro `ZEND_CALL_ARG(call, n)` /// zend_compile.h:578 /// - /// The resultant zval reference has a lifetime equal to the lifetime of `self`. - /// This isn't specified because when you attempt to get a reference to args and - /// the `$this` object, Rust doesnt't let you. Since this is a private method it's - /// up to the caller to ensure the lifetime isn't exceeded. + /// The resultant zval reference has a lifetime equal to the lifetime of + /// `self`. This isn't specified because when you attempt to get a + /// reference to args and the `$this` object, Rust doesnt't let you. + /// Since this is a private method it's up to the caller to ensure the + /// lifetime isn't exceeded. #[doc(hidden)] unsafe fn zend_call_arg<'a>(&self, n: usize) -> Option<&'a mut Zval> { let ptr = self.zend_call_var_num(n as isize); diff --git a/src/zend/function.rs b/src/zend/function.rs index 8088682c18..e9d9b08e9b 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -19,7 +19,8 @@ impl FunctionEntry { } } - /// Converts the function entry into a raw and pointer, releasing it to the C world. + /// Converts the function entry into a raw and pointer, releasing it to the + /// C world. pub fn into_raw(self) -> *mut Self { Box::into_raw(Box::new(self)) } diff --git a/src/zend/globals.rs b/src/zend/globals.rs index c12381a198..3a3b9677f5 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -31,7 +31,8 @@ impl ExecutorGlobals { /// Attempts to extract the last PHP exception captured by the interpreter. /// - /// Note that the caller is responsible for freeing the memory here or it'll leak. + /// Note that the caller is responsible for freeing the memory here or it'll + /// leak. pub fn take_exception() -> Option<*mut ZendObject> { let globals = Self::get_mut(); diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 6d600985a7..482ece76ab 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -15,8 +15,9 @@ use crate::{ pub type ZendObjectHandlers = zend_object_handlers; impl ZendObjectHandlers { - /// Initializes a given set of object handlers by copying the standard object handlers into - /// the memory location, as well as setting up the `T` type destructor. + /// Initializes a given set of object handlers by copying the standard + /// object handlers into the memory location, as well as setting up the + /// `T` type destructor. /// /// # Parameters /// @@ -205,6 +206,7 @@ impl ZendObjectHandlers { let self_ = &mut **obj; match has_set_exists { + // // * 0 (has) whether property exists and is not NULL 0 => { if let Some(val) = prop { @@ -215,6 +217,7 @@ impl ZendObjectHandlers { } } } + // // * 1 (set) whether property exists and is true 1 => { if let Some(val) = prop { @@ -226,6 +229,7 @@ impl ZendObjectHandlers { } } } + // // * 2 (exists) whether property exists 2 => { if prop.is_some() { diff --git a/src/zend/module.rs b/src/zend/module.rs index f48b2d1897..8723deed83 100644 --- a/src/zend/module.rs +++ b/src/zend/module.rs @@ -1,4 +1,5 @@ -//! Builder and objects for creating modules in PHP. A module is the base of a PHP extension. +//! Builder and objects for creating modules in PHP. A module is the base of a +//! PHP extension. use crate::ffi::zend_module_entry; @@ -6,7 +7,8 @@ use crate::ffi::zend_module_entry; pub type ModuleEntry = zend_module_entry; impl ModuleEntry { - /// Converts the module entry into a raw pointer, releasing it to the C world. + /// Converts the module entry into a raw pointer, releasing it to the C + /// world. pub fn into_raw(self) -> *mut Self { Box::into_raw(Box::new(self)) } From 51fac3c2c19820f51a7da9716d2cc01f9a6ae30d Mon Sep 17 00:00:00 2001 From: David Cole Date: Fri, 8 Oct 2021 00:42:47 +1300 Subject: [PATCH 07/10] Fixed up documentation links, tidied up --- crates/macros/src/function.rs | 2 +- crates/macros/src/method.rs | 4 +- src/args.rs | 2 +- src/boxed.rs | 2 +- src/builders/class.rs | 30 ++--- src/builders/function.rs | 10 +- src/builders/mod.rs | 3 + src/builders/module.rs | 9 +- src/class.rs | 23 ++-- src/closure.rs | 8 +- src/constant.rs | 3 +- src/convert.rs | 4 + src/error.rs | 7 +- src/exception.rs | 2 +- src/ffi.rs | 2 +- src/flags.rs | 2 +- src/internal.rs | 8 +- src/lib.rs | 4 +- src/macros.rs | 39 +++--- src/props.rs | 25 +++- src/rc.rs | 2 +- src/types/array.rs | 241 +++++++++++++++++++++++++++++++++- src/types/callable.rs | 38 +++++- src/types/class_object.rs | 3 +- src/types/long.rs | 7 +- src/types/mod.rs | 7 +- src/types/object.rs | 49 ++++--- src/types/string.rs | 118 +++++++++++++++-- src/types/zval.rs | 21 ++- src/zend/ce.rs | 9 +- src/zend/class.rs | 17 ++- src/zend/ex.rs | 133 +++++++++++++++++-- src/zend/function.rs | 4 +- src/zend/globals.rs | 98 +++++++++++--- src/zend/handlers.rs | 7 +- src/zend/mod.rs | 4 +- src/zend/module.rs | 6 +- 37 files changed, 779 insertions(+), 174 deletions(-) diff --git a/crates/macros/src/function.rs b/crates/macros/src/function.rs index d5901edb2c..cae941f46a 100644 --- a/crates/macros/src/function.rs +++ b/crates/macros/src/function.rs @@ -70,7 +70,7 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi #input #[doc(hidden)] - pub extern "C" fn #internal_ident(ex: &mut ::ext_php_rs::zend::ExecutionData, retval: &mut ::ext_php_rs::types::Zval) { + pub extern "C" fn #internal_ident(ex: &mut ::ext_php_rs::zend::ExecuteData, retval: &mut ::ext_php_rs::types::Zval) { use ::ext_php_rs::convert::IntoZval; #(#arg_definitions)* diff --git a/crates/macros/src/method.rs b/crates/macros/src/method.rs index 9b4dbb0963..9cc5de20ff 100644 --- a/crates/macros/src/method.rs +++ b/crates/macros/src/method.rs @@ -154,7 +154,7 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result ::ext_php_rs::class::ConstructorResult { use ::ext_php_rs::convert::IntoZval; use ::ext_php_rs::class::ConstructorResult; @@ -171,7 +171,7 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result ArgParser<'a, 'b> { } /// Uses the argument parser to parse the arguments contained in the given - /// `ExecutionData` object. Returns successfully if the arguments were + /// `ExecuteData` object. Returns successfully if the arguments were /// parsed. /// /// This function can only be safely called from within an exported PHP diff --git a/src/boxed.rs b/src/boxed.rs index 38623cf5d7..ae076160c2 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -18,7 +18,7 @@ //! returns a [`ZBox`]. //! //! [memory arenas]: https://en.wikipedia.org/wiki/Region-based_memory_management -//! [`ZendStr`]: super::types::string::ZendStr +//! [`ZendStr`]: crate::types::ZendStr //! [`emalloc`]: super::alloc::efree use std::{ diff --git a/src/builders/class.rs b/src/builders/class.rs index 04bc35db15..5d9267b430 100644 --- a/src/builders/class.rs +++ b/src/builders/class.rs @@ -1,4 +1,4 @@ -use std::{alloc::Layout, ffi::CString}; +use std::{ffi::CString, mem::MaybeUninit}; use crate::{ builders::FunctionBuilder, @@ -12,13 +12,13 @@ use crate::{ }, flags::{ClassFlags, MethodFlags, PropertyFlags}, types::{ZendClassObject, ZendObject, ZendStr, Zval}, - zend::{ClassEntry, ExecutionData, FunctionEntry}, + zend::{ClassEntry, ExecuteData, FunctionEntry}, }; -/// Builds a class to be exported as a PHP class. +/// Builder for registering a class in PHP. pub struct ClassBuilder { name: String, - ptr: &'static mut ClassEntry, + ptr: Box, extends: Option<&'static ClassEntry>, interfaces: Vec<&'static ClassEntry>, methods: Vec, @@ -34,16 +34,10 @@ impl ClassBuilder { /// # Parameters /// /// * `name` - The name of the class. - #[allow(clippy::unwrap_used)] pub fn new>(name: T) -> Self { - // SAFETY: Allocating temporary class entry. Will return a null-ptr if - // allocation fails, which will cause the program to panic (standard in - // Rust). Unwrapping is OK - the ptr will either be valid or null. - let ptr = unsafe { - (std::alloc::alloc_zeroed(Layout::new::()) as *mut ClassEntry) - .as_mut() - .unwrap() - }; + // SAFETY: A zeroed class entry is in an initalized state, as it is a raw C type + // whose fields do not have a drop implementation. + let ptr = unsafe { Box::new(MaybeUninit::zeroed().assume_init()) }; Self { name: name.into(), @@ -175,7 +169,7 @@ impl ClassBuilder { obj.into_raw().get_mut_zend_obj() } - extern "C" fn constructor(ex: &mut ExecutionData, _: &mut Zval) { + extern "C" fn constructor(ex: &mut ExecuteData, _: &mut Zval) { let ConstructorMeta { constructor, .. } = match T::CONSTRUCTOR { Some(c) => c, None => { @@ -239,7 +233,7 @@ impl ClassBuilder { let class = unsafe { zend_register_internal_class_ex( - self.ptr, + self.ptr.as_mut(), match self.extends { Some(ptr) => (ptr as *const _) as *mut _, None => std::ptr::null_mut(), @@ -249,12 +243,6 @@ impl ClassBuilder { .ok_or(Error::InvalidPointer)? }; - // SAFETY: We allocated memory for this pointer in `new`, so it is our job to - // free it when the builder has finished. - unsafe { - std::alloc::dealloc((self.ptr as *mut _) as *mut u8, Layout::new::()) - }; - for iface in self.interfaces { unsafe { zend_do_implement_interface(class, std::mem::transmute(iface)) }; } diff --git a/src/builders/function.rs b/src/builders/function.rs index f57797d124..d448d01ba6 100644 --- a/src/builders/function.rs +++ b/src/builders/function.rs @@ -3,17 +3,17 @@ use crate::{ error::{Error, Result}, flags::DataType, types::Zval, - zend::{ExecutionData, FunctionEntry, ZendType}, + zend::{ExecuteData, FunctionEntry, ZendType}, }; use std::{ffi::CString, mem, ptr}; /// Function representation in Rust. -pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecutionData, retval: &mut Zval); +pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecuteData, retval: &mut Zval); /// Function representation in Rust using pointers. -type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecutionData, retval: *mut Zval); +type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecuteData, retval: *mut Zval); -/// Builds a function to be exported as a PHP function. +/// Builder for registering a function in PHP. #[derive(Debug)] pub struct FunctionBuilder<'a> { name: String, @@ -39,6 +39,8 @@ impl<'a> FunctionBuilder<'a> { name: name.into(), function: FunctionEntry { fname: ptr::null(), + // SAFETY: `*mut T` and `&mut T` have the same ABI as long as `*mut T` is non-null, + // aligned and pointing to a `T`. PHP guarantees that these conditions will be met. handler: Some(unsafe { mem::transmute::(handler) }), diff --git a/src/builders/mod.rs b/src/builders/mod.rs index b803872b19..66ad38942b 100644 --- a/src/builders/mod.rs +++ b/src/builders/mod.rs @@ -1,3 +1,6 @@ +//! Structures that are used to construct other, more complicated types. +//! Generally zero-cost abstractions. + mod class; mod function; mod module; diff --git a/src/builders/module.rs b/src/builders/module.rs index 8d31f09191..265e71580e 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -9,8 +9,9 @@ use std::{ mem, ptr, }; -/// Builds a Zend extension. Must be called from within an external function -/// called `get_module`, returning a mutable pointer to a `ModuleEntry`. +/// Builds a Zend module extension to be registered with PHP. Must be called +/// from within an external function called `get_module`, returning a mutable +/// pointer to a `ModuleEntry`. /// /// ``` /// use ext_php_rs::{ @@ -49,8 +50,7 @@ impl ModuleBuilder { /// # Arguments /// /// * `name` - The name of the extension. - /// * `version` - The current version of the extension. TBD: Deprecate in - /// favour of the `Cargo.toml` version? + /// * `version` - The current version of the extension. pub fn new, U: Into>(name: T, version: U) -> Self { Self { name: name.into(), @@ -165,5 +165,6 @@ impl ModuleBuilder { /// A function to be called when the extension is starting up or shutting down. pub type StartupShutdownFunc = extern "C" fn(_type: i32, _module_number: i32) -> i32; + /// A function to be called when `phpinfo();` is called. pub type InfoFunc = extern "C" fn(zend_module: *mut ModuleEntry); diff --git a/src/class.rs b/src/class.rs index 70a006b96d..8c0f9aa8ab 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,3 +1,5 @@ +//! Types and traits used for registering classes with PHP. + use std::{ collections::HashMap, marker::PhantomData, @@ -11,7 +13,7 @@ use crate::{ exception::PhpException, props::Property, types::ZendClassObject, - zend::{ClassEntry, ExecutionData, ZendObjectHandlers}, + zend::{ClassEntry, ExecuteData, ZendObjectHandlers}, }; /// Implemented on Rust types which are exported to PHP. Allows users to get and @@ -49,8 +51,10 @@ where /// # Safety /// /// Caller must guarantee that the object the function is called on is - /// immediately followed by a [`zend_object`], which is true when the + /// immediately followed by a [`ZendObject`], which is true when the /// object was instantiated by PHP. + /// + /// [`ZendObject`]: crate::types::ZendObject unsafe fn get_property<'a, T: FromZval<'a>>(&'a self, name: &str) -> Option { let obj = ZendClassObject::::from_obj_ptr(self)?; obj.std.get_property(name).ok() @@ -71,8 +75,10 @@ where /// # Safety /// /// Caller must guarantee that the object the function is called on is - /// immediately followed by a [`zend_object`], which is true when the + /// immediately followed by a [`ZendObject`], which is true when the /// object was instantiated by PHP. + /// + /// [`ZendObject`]: crate::types::ZendObject unsafe fn set_property(&mut self, name: &str, value: impl IntoZval) -> Option<()> { let obj = ZendClassObject::::from_obj_ptr(self)?; obj.std.set_property(name, value).ok()?; @@ -87,10 +93,11 @@ where fn get_properties<'a>() -> HashMap<&'static str, Property<'a, Self>>; } -/// Object constructor metadata. +/// Stores metadata about a classes Rust constructor, including the function +/// pointer and the arguments of the function. pub struct ConstructorMeta { /// Constructor function. - pub constructor: fn(&mut ExecutionData) -> ConstructorResult, + pub constructor: fn(&mut ExecuteData) -> ConstructorResult, /// Function called to build the constructor function. Usually adds /// arguments. pub build_fn: fn(FunctionBuilder) -> FunctionBuilder, @@ -125,7 +132,7 @@ impl From for ConstructorResult { } /// Stores the class entry and handlers for a Rust type which has been exported -/// to PHP. +/// to PHP. Usually allocated statically. pub struct ClassMetadata { handlers_init: AtomicBool, handlers: MaybeUninit, @@ -195,10 +202,10 @@ impl ClassMetadata { /// Checks if the handlers have been initialized, and initializes them if /// they are not. fn check_handlers(&self) { - if !self.handlers_init.load(Ordering::Acquire) { + if !self.handlers_init.load(Ordering::SeqCst) { // SAFETY: `MaybeUninit` has the same size as the handlers. unsafe { ZendObjectHandlers::init::(self.handlers.as_ptr() as *mut _) }; - self.handlers_init.store(true, Ordering::Release); + self.handlers_init.store(true, Ordering::SeqCst); } } } diff --git a/src/closure.rs b/src/closure.rs index 1ca93a4711..9767d9d3cf 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -1,4 +1,4 @@ -//! Types ans functions used for exporting Rust closures to PHP. +//! Types and functions used for exporting Rust closures to PHP. use std::collections::HashMap; @@ -11,7 +11,7 @@ use crate::{ flags::{DataType, MethodFlags}, props::Property, types::Zval, - zend::ExecutionData, + zend::ExecuteData, }; /// Class entry and handlers for Rust closures. @@ -138,7 +138,7 @@ impl Closure { } /// External function used by the Zend interpreter to call the closure. - extern "C" fn invoke(ex: &mut ExecutionData, ret: &mut Zval) { + extern "C" fn invoke(ex: &mut ExecuteData, ret: &mut Zval) { let (parser, this) = ex.parser_method::(); let this = this.expect("Internal closure function called on non-closure class"); @@ -164,7 +164,7 @@ class_derives!(Closure); /// /// Types must implement the `invoke` function which will be called when the /// closure is called from PHP. Arguments must be parsed from the -/// [`ExecutionData`] and the return value is returned through the [`Zval`]. +/// [`ExecuteData`] and the return value is returned through the [`Zval`]. /// /// This trait is automatically implemented on functions with up to 8 /// parameters. diff --git a/src/constant.rs b/src/constant.rs index fda5e446f6..03b5043226 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -1,4 +1,4 @@ -//! Types relating to registering constants in PHP. +//! Types and traits for registering constants in PHP. use std::ffi::CString; @@ -9,6 +9,7 @@ use crate::ffi::{ zend_register_string_constant, }; +/// Implemented on types which can be registered as a constant in PHP. pub trait IntoConst: Sized { /// Registers a global module constant in PHP, with the value as the content /// of self. This function _must_ be called in the module startup diff --git a/src/convert.rs b/src/convert.rs index 9db5de7592..1b0347726b 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,3 +1,5 @@ +//! Traits used to convert between Zend/PHP and Rust types. + use crate::{ boxed::ZBox, error::Result, @@ -111,6 +113,8 @@ pub trait IntoZendObject { /// Alternative to the built-in Rust [`From`] and [`TryFrom`] implementations, /// allowing the caller to specify whether the Zval contents will persist /// between requests. +/// +/// [`TryFrom`]: std::convert::TryFrom pub trait IntoZval: Sized { /// The corresponding type of the implemented value in PHP. const TYPE: DataType; diff --git a/src/error.rs b/src/error.rs index 15e77def60..eb24096cb9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,8 +3,10 @@ use std::{error::Error as ErrorTrait, ffi::NulError, fmt::Display}; use crate::{ + boxed::ZBox, exception::PhpException, flags::{ClassFlags, DataType, ZvalTypeFlags}, + types::ZendObject, }; /// The main result type which is passed by the library. @@ -12,7 +14,7 @@ pub type Result = std::result::Result; /// The main error type which is passed by the library inside the custom /// [`Result`] type. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug)] #[non_exhaustive] pub enum Error { /// An incorrect number of arguments was given to a PHP function. @@ -52,6 +54,8 @@ pub enum Error { InvalidException(ClassFlags), /// Converting integer arguments resulted in an overflow. IntegerOverflow, + /// An exception was thrown in a function. + Exception(ZBox), } impl Display for Error { @@ -85,6 +89,7 @@ impl Display for Error { Error::IntegerOverflow => { write!(f, "Converting integer arguments resulted in an overflow.") } + Error::Exception(e) => write!(f, "Exception was thrown: {:?}", e), } } } diff --git a/src/exception.rs b/src/exception.rs index e622e0748f..7ec2d7060d 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,4 +1,4 @@ -//! Contains all the base PHP throwables, including `Throwable` and `Exception`. +//! Types and functions used for throwing exceptions from Rust to PHP. use std::ffi::CString; diff --git a/src/ffi.rs b/src/ffi.rs index 93b67ecd58..46f9e7431a 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,4 +1,4 @@ -//! Raw FFI bindings to the PHP API. +//! Raw FFI bindings to the Zend API. #![allow(clippy::all)] #![allow(warnings)] diff --git a/src/flags.rs b/src/flags.rs index e197b2084c..b9cf4f1498 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -1,4 +1,4 @@ -//! Bitflags used in PHP and the Zend engine. +//! Flags and enums used in PHP and the Zend engine. use bitflags::bitflags; diff --git a/src/internal.rs b/src/internal.rs index bc581f85ef..44d9af34d8 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,7 +1,9 @@ -/// Called by startup functions registered with the `#[php_startup]` macro. -/// Initializes all classes that are defined by ext-php-rs (i.e. [`Closure`]). +//! Internal, public functions that are called from downstream extensions. + +/// Called by startup functions registered with the [`#[php_startup]`] macro. +/// Initializes all classes that are defined by ext-php-rs (i.e. `Closure`). /// -/// [`Closure`]: ext_php_rs::php::types::closure::Closure +/// [`#[php_startup]`]: crate::php_startup #[inline(always)] pub fn ext_php_rs_startup() { #[cfg(feature = "closure")] diff --git a/src/lib.rs b/src/lib.rs index ef9cb314a6..577242ad83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,12 +170,12 @@ pub use ext_php_rs_derive::php_extern; /// function which looks like so: /// /// ```no_run -/// # use ext_php_rs::{prelude::*, exception::PhpException, zend::ExecutionData, convert::{FromZval, IntoZval}, types::Zval, args::{Arg, ArgParser}}; +/// # use ext_php_rs::{prelude::*, exception::PhpException, zend::ExecuteData, convert::{FromZval, IntoZval}, types::Zval, args::{Arg, ArgParser}}; /// pub fn hello(name: String) -> String { /// format!("Hello, {}!", name) /// } /// -/// pub extern "C" fn _internal_php_hello(ex: &mut ExecutionData, retval: &mut Zval) { +/// pub extern "C" fn _internal_php_hello(ex: &mut ExecuteData, retval: &mut Zval) { /// let mut name = Arg::new("name", ::TYPE); /// let parser = ex.parser() /// .arg(&mut name) diff --git a/src/macros.rs b/src/macros.rs index d372dd930e..971adbeeff 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -55,12 +55,12 @@ macro_rules! _info_table_row { /// /// # Parameters /// -/// * `$fn` - The 'function' to call. Can be an [`Arg`](crate::php::args::Arg) -/// or a -/// [`Zval`](crate::php::types::zval::Zval). +/// * `$fn` - The 'function' to call. Can be an [`Arg`] or a [`Zval`]. /// * ...`$param` - The parameters to pass to the function. Must be able to be -/// converted into a -/// [`Zval`](crate::php::types::zval::Zval). +/// converted into a [`Zval`]. +/// +/// [`Arg`]: crate::args::Arg +/// [`Zval`]: crate::types::Zval #[macro_export] macro_rules! call_user_func { ($fn: expr) => { @@ -72,8 +72,7 @@ macro_rules! call_user_func { }; } -/// Parses a given list of arguments using the -/// [`ArgParser`](crate::php::args::ArgParser) class. +/// Parses a given list of arguments using the [`ArgParser`] class. /// /// # Examples /// @@ -89,11 +88,11 @@ macro_rules! call_user_func { /// parse_args, /// args::Arg, /// flags::DataType, -/// zend::ExecutionData, +/// zend::ExecuteData, /// types::Zval, /// }; /// -/// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { +/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) { /// let mut x = Arg::new("x", DataType::Long); /// let mut y = Arg::new("y", DataType::Long); /// let mut z = Arg::new("z", DataType::Long); @@ -111,11 +110,11 @@ macro_rules! call_user_func { /// parse_args, /// args::Arg, /// flags::DataType, -/// zend::ExecutionData, +/// zend::ExecuteData, /// types::Zval, /// }; /// -/// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { +/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) { /// let mut x = Arg::new("x", DataType::Long); /// let mut y = Arg::new("y", DataType::Long); /// let mut z = Arg::new("z", DataType::Long); @@ -123,6 +122,8 @@ macro_rules! call_user_func { /// parse_args!(execute_data, x, y; z); /// } /// ``` +/// +/// [`ArgParser`]: crate::args::ArgParser #[macro_export] macro_rules! parse_args { ($ed: expr, $($arg: expr),*) => {{ @@ -151,18 +152,18 @@ macro_rules! parse_args { /// Wraps the [`throw`] function by inserting a `return` statement after /// throwing the exception. /// -/// [`throw`]: crate::php::exceptions::throw +/// [`throw`]: crate::exception::throw /// /// # Examples /// /// ``` /// use ext_php_rs::{ /// throw, -/// zend::{ce, ClassEntry, ExecutionData}, +/// zend::{ce, ClassEntry, ExecuteData}, /// types::Zval, /// }; /// -/// pub extern "C" fn example_fn(execute_data: &mut ExecutionData, _: &mut Zval) { +/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) { /// let something_wrong = true; /// if something_wrong { /// throw!(ce::exception(), "Something is wrong!"); @@ -182,7 +183,7 @@ macro_rules! throw { /// Implements a set of traits required to convert types that implement /// [`RegisteredClass`] to and from [`ZendObject`]s and [`Zval`]s. Generally, /// this macro should not be called directly, as it is called on any type that -/// uses the [`#[php_class]`] macro. +/// uses the [`php_class`] macro. /// /// The following traits are implemented: /// @@ -235,10 +236,10 @@ macro_rules! throw { /// } /// ``` /// -/// [`RegisteredClass`]: crate::php::types::object::RegisteredClass -/// [`ZendObject`]: crate::php::types::object::ZendObject -/// [`Zval`]: crate::php::types::zval::Zval -/// [`#[php_class]`]: crate::php_class +/// [`RegisteredClass`]: crate::class::RegisteredClass +/// [`ZendObject`]: crate::types::ZendObject +/// [`Zval`]: crate::types::Zval +/// [`php_class`]: crate::php_class #[macro_export] macro_rules! class_derives { ($type: ty) => { diff --git a/src/props.rs b/src/props.rs index aaeef0aff6..d515833f25 100644 --- a/src/props.rs +++ b/src/props.rs @@ -1,4 +1,23 @@ -//! Utilities for adding properties to classes. +//! Types and traits for adding properties to PHP classes registered from Rust. +//! +//! There are two types of properties: +//! +//! * Field properties, referencing a property on a struct. +//! * Method properties, a getter and/or setter function called to get/set the +//! value of the property. +//! +//! Field types which can be used as a property implement [`Prop`]. This is +//! automatically implemented on any type which implements [`Clone`], +//! [`IntoZval`] and [`FromZval`]. +//! +//! Method property types only have to implement [`IntoZval`] for setters and +//! [`FromZval`] for getters. +//! +//! Properties are stored in the [`Property`] type, which allows us to store +//! field and method properties in one data structure. Properties are usually +//! retrieved via the [`RegisteredClass`] trait. +//! +//! [`RegisteredClass`]: crate::class::RegisteredClass use crate::{ convert::{FromZval, IntoZval}, @@ -169,7 +188,7 @@ impl<'a, T: 'a> Property<'a, T> { /// assert_eq!(zv.long(), Some(500)); /// ``` /// - /// [`PhpException`]: crate::php::exceptions::PhpException + /// [`PhpException`]: crate::exception::PhpException pub fn get(&self, self_: &'a mut T, retval: &mut Zval) -> PhpResult { match self { Property::Field(field) => field(self_) @@ -217,7 +236,7 @@ impl<'a, T: 'a> Property<'a, T> { /// assert_eq!(test.a, 100); /// ``` /// - /// [`PhpException`]: crate::php::exceptions::PhpException + /// [`PhpException`]: crate::exception::PhpException pub fn set(&self, self_: &'a mut T, value: &Zval) -> PhpResult { match self { Property::Field(field) => field(self_) diff --git a/src/rc.rs b/src/rc.rs index 689f8f90ba..2842b2c247 100644 --- a/src/rc.rs +++ b/src/rc.rs @@ -1,4 +1,4 @@ -//! Utilities for interacting with refcounted PHP types. +//! Traits and types for interacting with reference counted PHP types. use crate::{ ffi::{zend_refcounted_h, zend_string}, diff --git a/src/types/array.rs b/src/types/array.rs index 336ffa5ad8..de028ef270 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -25,27 +25,76 @@ use crate::{ types::Zval, }; -/// PHP array, which is represented in memory as a hashtable. +/// A PHP hashtable. +/// +/// In PHP, arrays are represented as hashtables. This allows you to push values +/// onto the end of the array like a vector, while also allowing you to insert +/// at arbitrary string key indexes. +/// +/// A PHP hashtable stores values as [`Zval`]s. This allows you to insert +/// different types into the same hashtable. Types must implement [`IntoZval`] +/// to be able to be inserted into the hashtable. +/// +/// # Examples +/// +/// ```no_run +/// use ext_php_rs::types::HashTable; +/// +/// let mut ht = HashTable::new(); +/// ht.push(1); +/// ht.push("Hello, world!"); +/// ht.insert("Like", "Hashtable"); +/// +/// assert_eq!(ht.len(), 3); +/// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(1)); +/// ``` pub type HashTable = crate::ffi::HashTable; // Clippy complains about there being no `is_empty` function when implementing // on the alias `ZendStr` :( #[allow(clippy::len_without_is_empty)] impl HashTable { - /// Creates a new, empty, PHP associative array. + /// Creates a new, empty, PHP hashtable, returned inside a [`ZBox`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let ht = HashTable::new(); + /// ``` + /// + /// # Panics + /// + /// Panics if memory for the hashtable could not be allocated. pub fn new() -> ZBox { Self::with_capacity(HT_MIN_SIZE) } - /// Creates a new, empty, PHP associative array with an initial size. + /// Creates a new, empty, PHP hashtable with an initial size, returned + /// inside a [`ZBox`]. /// /// # Parameters /// /// * `size` - The size to initialize the array with. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let ht = HashTable::with_capacity(10); + /// ``` + /// + /// # Panics + /// + /// Panics if memory for the hashtable could not be allocated. pub fn with_capacity(size: u32) -> ZBox { - // SAFETY: PHP allocater handles the creation of the array. unsafe { + // SAFETY: PHP allocater handles the creation of the array. let ptr = _zend_new_array(size); + + // SAFETY: `as_mut()` checks if the pointer is null, and panics if it is not. ZBox::from_raw( ptr.as_mut() .expect("Failed to allocate memory for hashtable"), @@ -54,16 +103,58 @@ impl HashTable { } /// Returns the current number of elements in the array. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.push(1); + /// ht.push("Hello, world"); + /// + /// assert_eq!(ht.len(), 2); + /// ``` pub fn len(&self) -> usize { self.nNumOfElements as usize } /// Returns whether the hash table is empty. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// assert_eq!(ht.is_empty(), true); + /// + /// ht.push(1); + /// ht.push("Hello, world"); + /// + /// assert_eq!(ht.is_empty(), false); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Clears the hash table, removing all values. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.insert("test", "hello world"); + /// assert_eq!(ht.is_empty(), false); + /// + /// ht.clear(); + /// assert_eq!(ht.is_empty(), true); + /// ``` pub fn clear(&mut self) { unsafe { zend_hash_clean(self) } } @@ -79,6 +170,17 @@ impl HashTable { /// * `Some(&Zval)` - A reference to the zval at the position in the hash /// table. /// * `None` - No value at the given position was found. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.insert("test", "hello world"); + /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world")); + /// ``` pub fn get(&self, key: &'_ str) -> Option<&Zval> { let str = CString::new(key).ok()?; unsafe { zend_hash_str_find(self, str.as_ptr(), key.len() as _).as_ref() } @@ -95,6 +197,17 @@ impl HashTable { /// * `Some(&Zval)` - A reference to the zval at the position in the hash /// table. /// * `None` - No value at the given position was found. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.push(100); + /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100)); + /// ``` pub fn get_index(&self, key: u64) -> Option<&Zval> { unsafe { zend_hash_index_find(self, key).as_ref() } } @@ -109,7 +222,21 @@ impl HashTable { /// /// * `Some(())` - Key was successfully removed. /// * `None` - No key was removed, did not exist. - pub fn remove(&mut self, key: &str) -> Option<()> { + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.insert("test", "hello world"); + /// assert_eq!(ht.len(), 1); + /// + /// ht.remove("test"); + /// assert_eq!(ht.len(), 0); + /// ``` + pub fn remove(&mut self, key: &str) -> Option<()> { let result = unsafe { zend_hash_str_del(self, CString::new(key).ok()?.as_ptr(), key.len() as _) }; @@ -130,6 +257,20 @@ impl HashTable { /// /// * `Ok(())` - Key was successfully removed. /// * `None` - No key was removed, did not exist. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.push("hello"); + /// assert_eq!(ht.len(), 1); + /// + /// ht.remove_index(0); + /// assert_eq!(ht.len(), 0); + /// ``` pub fn remove_index(&mut self, key: u64) -> Option<()> { let result = unsafe { zend_hash_index_del(self, key) }; @@ -147,6 +288,25 @@ impl HashTable { /// /// * `key` - The key to insert the value at in the hash table. /// * `value` - The value to insert into the hash table. + /// + /// # Returns + /// + /// Returns nothing in a result on success. Returns an error if the key + /// could not be converted into a [`CString`], or converting the value into + /// a [`Zval`] failed. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.insert("a", "A"); + /// ht.insert("b", "B"); + /// ht.insert("c", "C"); + /// assert_eq!(ht.len(), 3); + /// ``` pub fn insert(&mut self, key: &str, val: V) -> Result<()> where V: IntoZval, @@ -171,6 +331,24 @@ impl HashTable { /// /// * `key` - The index at which the value should be inserted. /// * `val` - The value to insert into the hash table. + /// + /// # Returns + /// + /// Returns nothing in a result on success. Returns an error if converting + /// the value into a [`Zval`] failed. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.insert_at_index(0, "A"); + /// ht.insert_at_index(5, "B"); + /// ht.insert_at_index(0, "C"); // notice overriding index 0 + /// assert_eq!(ht.len(), 2); + /// ``` pub fn insert_at_index(&mut self, key: u64, val: V) -> Result<()> where V: IntoZval, @@ -187,6 +365,24 @@ impl HashTable { /// # Parameters /// /// * `val` - The value to insert into the hash table. + /// + /// # Returns + /// + /// Returns nothing in a result on success. Returns an error if converting + /// the value into a [`Zval`] failed. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// ht.push("a"); + /// ht.push("b"); + /// ht.push("c"); + /// assert_eq!(ht.len(), 3); + /// ``` pub fn push(&mut self, val: V) -> Result<()> where V: IntoZval, @@ -200,6 +396,21 @@ impl HashTable { /// Returns an iterator over the key(s) and value contained inside the /// hashtable. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// for (idx, key, val) in ht.iter() { + /// // ^ Index if inserted at an index. + /// // ^ Optional string key, if inserted like a hashtable. + /// // ^ Inserted value. + /// + /// dbg!(idx, key, val); + /// } #[inline] pub fn iter(&self) -> Iter { Iter::new(self) @@ -207,6 +418,17 @@ impl HashTable { /// Returns an iterator over the values contained inside the hashtable, as /// if it was a set or list. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::HashTable; + /// + /// let mut ht = HashTable::new(); + /// + /// for val in ht.values() { + /// dbg!(val); + /// } #[inline] pub fn values(&self) -> Values { Values::new(self) @@ -215,6 +437,7 @@ impl HashTable { unsafe impl ZBoxable for HashTable { fn free(&mut self) { + // SAFETY: ZBox has immutable access to `self`. unsafe { zend_array_destroy(self) } } } @@ -235,8 +458,14 @@ impl ToOwned for HashTable { fn to_owned(&self) -> Self::Owned { unsafe { + // SAFETY: FFI call does not modify `self`, returns a new hashtable. let ptr = zend_array_dup(self as *const HashTable as *mut HashTable); - ZBox::from_raw(ptr) + + // SAFETY: `as_mut()` checks if the pointer is null, and panics if it is not. + ZBox::from_raw( + ptr.as_mut() + .expect("Failed to allocate memory for hashtable"), + ) } } } diff --git a/src/types/callable.rs b/src/types/callable.rs index 2b2ac35378..15088db1e4 100644 --- a/src/types/callable.rs +++ b/src/types/callable.rs @@ -7,13 +7,15 @@ use crate::{ error::{Error, Result}, ffi::_call_user_function_impl, flags::DataType, + zend::ExecutorGlobals, }; use super::Zval; /// Acts as a wrapper around a callable [`Zval`]. Allows the owner to call the -/// [`Zval`] as if it was a PHP function through the -/// [`try_call`](Callable::try_call) method. +/// [`Zval`] as if it was a PHP function through the [`try_call`] method. +/// +/// [`try_call`]: #method.try_call #[derive(Debug)] pub struct Callable<'a>(OwnedZval<'a>); @@ -56,6 +58,16 @@ impl<'a> Callable<'a> { /// # Parameters /// /// * `name` - Name of the callable function. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::Callable; + /// + /// let strpos = Callable::try_from_name("strpos").unwrap(); + /// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap(); + /// assert_eq!(result.long(), Some(1)); + /// ``` pub fn try_from_name(name: &str) -> Result { let mut callable = Zval::new(); callable.set_string(name, false)?; @@ -64,10 +76,7 @@ impl<'a> Callable<'a> { } /// Attempts to call the callable with a list of arguments to pass to the - /// function. Note that a thrown exception inside the callable is not - /// detectable, therefore you should check if the return value is valid - /// rather than unwrapping. Returns a result containing the return value - /// of the function, or an error. + /// function. /// /// You should not call this function directly, rather through the /// [`call_user_func`] macro. @@ -75,6 +84,21 @@ impl<'a> Callable<'a> { /// # Parameters /// /// * `params` - A list of parameters to call the function with. + /// + /// # Returns + /// + /// Returns the result wrapped in [`Ok`] upon success. If calling the + /// callable fails, or an exception is thrown, an [`Err`] is returned. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::Callable; + /// + /// let strpos = Callable::try_from_name("strpos").unwrap(); + /// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap(); + /// assert_eq!(result.long(), Some(1)); + /// ``` pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result { if !self.0.is_callable() { return Err(Error::Callable); @@ -101,6 +125,8 @@ impl<'a> Callable<'a> { if result < 0 { Err(Error::Callable) + } else if let Some(e) = ExecutorGlobals::take_exception() { + Err(Error::Exception(e)) } else { Ok(retval) } diff --git a/src/types/class_object.rs b/src/types/class_object.rs index 33e41767fe..84646a634e 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -3,7 +3,7 @@ use std::{ fmt::Debug, - mem::{self}, + mem, ops::{Deref, DerefMut}, ptr::{self, NonNull}, }; @@ -41,6 +41,7 @@ impl ZendClassObject { /// /// Panics if memory was unable to be allocated for the new object. pub fn new(val: T) -> ZBox { + // SAFETY: We are providing a value to initialize the object with. unsafe { Self::internal_new(Some(val)) } } diff --git a/src/types/long.rs b/src/types/long.rs index 806663bbbf..13cbb9b62c 100644 --- a/src/types/long.rs +++ b/src/types/long.rs @@ -13,9 +13,10 @@ use crate::{ use std::convert::{TryFrom, TryInto}; -/// Internal identifier used for a long. -/// The size depends on the system architecture. On 32-bit systems, -/// a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. +/// A PHP long. +/// +/// The type size depends on the system architecture. On 32-bit systems, it is +/// 32-bits, while on a 64-bit system, it is 64-bits. pub type ZendLong = zend_long; into_zval!(i8, set_long, Long); diff --git a/src/types/mod.rs b/src/types/mod.rs index 4a728d635d..80bf8666ec 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,3 +1,8 @@ +//! Types defined by the Zend engine used in PHP. +//! +//! Generally, it is easier to work directly with Rust types, converting into +//! these PHP types when required. + mod array; mod callable; mod class_object; @@ -10,7 +15,7 @@ pub use array::HashTable; pub use callable::Callable; pub use class_object::ZendClassObject; pub use long::ZendLong; -pub use object::ZendObject; +pub use object::{PropertyQuery, ZendObject}; pub use string::ZendStr; pub use zval::Zval; diff --git a/src/types/object.rs b/src/types/object.rs index 4272d682c4..d946ee6610 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -10,15 +10,20 @@ use crate::{ error::{Error, Result}, ffi::{ ext_php_rs_zend_object_release, zend_call_known_function, zend_object, zend_objects_new, - zend_standard_class_def, HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, - ZEND_PROPERTY_ISSET, + HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET, }, flags::DataType, rc::PhpRc, types::{ZendClassObject, ZendStr, Zval}, - zend::{ClassEntry, ExecutorGlobals, ZendObjectHandlers}, + zend::{ce, ClassEntry, ExecutorGlobals, ZendObjectHandlers}, }; +/// A PHP object. +/// +/// This type does not maintain any information about its type, for example, +/// classes with have associated Rust structs cannot be accessed through this +/// type. [`ZendClassObject`] is used for this purpose, and you can convert +/// between the two. pub type ZendObject = zend_object; impl ZendObject { @@ -51,17 +56,23 @@ impl ZendObject { /// /// Panics if allocating memory for the object fails, or if the `stdClass` /// class entry has not been registered with PHP yet. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendObject; + /// + /// let mut obj = ZendObject::new_stdclass(); + /// + /// obj.set_property("hello", "world"); + /// ``` pub fn new_stdclass() -> ZBox { // SAFETY: This will be `NULL` until it is initialized. `as_ref()` checks for // null, so we can panic if it's null. - Self::new(unsafe { - zend_standard_class_def - .as_ref() - .expect("`stdClass` class instance not initialized yet") - }) + Self::new(ce::stdclass()) } - /// Converts the class object into an owned [`ZendObject`]. This removes any + /// Converts a class object into an owned [`ZendObject`]. This removes any /// possibility of accessing the underlying attached Rust struct. pub fn from_class_object(obj: ZBox>) -> ZBox { let this = obj.into_raw(); @@ -175,6 +186,16 @@ impl ZendObject { } } + /// Extracts some type from a Zend object. + /// + /// This is a wrapper function around `FromZendObject::extract()`. + pub fn extract<'a, T>(&'a self) -> Result + where + T: FromZendObject<'a>, + { + T::from_zend_object(self) + } + /// Attempts to retrieve a reference to the object handlers. #[inline] unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> { @@ -188,16 +209,6 @@ impl ZendObject { fn mut_ptr(&self) -> *mut Self { (self as *const Self) as *mut Self } - - /// Extracts some type from a Zend object. - /// - /// This is a wrapper function around `FromZendObject::extract()`. - pub fn extract<'a, T>(&'a self) -> Result - where - T: FromZendObject<'a>, - { - T::from_zend_object(self) - } } unsafe impl ZBoxable for ZendObject { diff --git a/src/types/string.rs b/src/types/string.rs index af67bcb087..6296c735fc 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -1,6 +1,5 @@ //! Represents a string in the PHP world. Similar to a C string, but is -//! reference counted and contains the length of the string, meaning the string -//! can contain the NUL character. +//! reference counted and contains the length of the string. use std::{ borrow::Cow, @@ -10,10 +9,7 @@ use std::{ slice, }; -use parking_lot::{ - lock_api::{Mutex, RawMutex}, - RawMutex as RawMutexStruct, -}; +use parking_lot::{const_mutex, Mutex}; use crate::{ boxed::{ZBox, ZBoxable}, @@ -28,7 +24,7 @@ use crate::{ types::Zval, }; -/// A borrowed Zend-string. +/// A borrowed Zend string. /// /// Although this object does implement [`Sized`], it is in fact not sized. As C /// cannot represent unsized types, an array of size 1 is used at the end of the @@ -43,8 +39,8 @@ pub type ZendStr = zend_string; // Adding to the Zend interned string hashtable is not atomic and can be // contested when PHP is compiled with ZTS, so an empty mutex is used to ensure // no collisions occur on the Rust side. Not much we can do about collisions -// on the PHP side. -static INTERNED_LOCK: Mutex = Mutex::const_new(RawMutex::INIT, ()); +// on the PHP side, but some safety is better than none. +static INTERNED_LOCK: Mutex<()> = const_mutex(()); // Clippy complains about there being no `is_empty` function when implementing // on the alias `ZendStr` :( @@ -68,6 +64,22 @@ impl ZendStr { /// /// Panics if the function was unable to allocate memory for the Zend /// string. + /// + /// # Safety + /// + /// When passing `persistent` as `false`, the caller must ensure that the + /// object does not attempt to live after the request finishes. When a + /// request starts and finishes in PHP, the Zend heap is deallocated and a + /// new one is created, which would leave a dangling pointer in the + /// [`ZBox`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// + /// let s = ZendStr::new("Hello, world!", false).unwrap(); + /// ``` pub fn new(str: &str, persistent: bool) -> Result> { Ok(Self::from_c_str(&CString::new(str)?, persistent)) } @@ -84,6 +96,24 @@ impl ZendStr { /// /// Panics if the function was unable to allocate memory for the Zend /// string. + /// + /// # Safety + /// + /// When passing `persistent` as `false`, the caller must ensure that the + /// object does not attempt to live after the request finishes. When a + /// request starts and finishes in PHP, the Zend heap is deallocated and a + /// new one is created, which would leave a dangling pointer in the + /// [`ZBox`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// use std::ffi::CString; + /// + /// let c_s = CString::new("Hello world!").unwrap(); + /// let s = ZendStr::from_c_str(&c_s, false); + /// ``` pub fn from_c_str(str: &CStr, persistent: bool) -> ZBox { unsafe { let ptr = @@ -105,6 +135,10 @@ impl ZendStr { /// As Zend hashtables are not thread-safe, a mutex is used to prevent two /// interned strings from being created at the same time. /// + /// Interned strings are not used very often. You should almost always use a + /// regular zend string, except in the case that you know you will use a + /// string that PHP will already have interned, such as "PHP". + /// /// # Parameters /// /// * `str` - String content. @@ -121,6 +155,22 @@ impl ZendStr { /// /// Panics if the function was unable to allocate memory for the Zend /// string. + /// + /// # Safety + /// + /// When passing `persistent` as `false`, the caller must ensure that the + /// object does not attempt to live after the request finishes. When a + /// request starts and finishes in PHP, the Zend heap is deallocated and a + /// new one is created, which would leave a dangling pointer in the + /// [`ZBox`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// + /// let s = ZendStr::new_interned("PHP", true); + /// ``` pub fn new_interned(str: &str, persistent: bool) -> Result> { Ok(Self::interned_from_c_str(&CString::new(str)?, persistent)) } @@ -134,6 +184,10 @@ impl ZendStr { /// As Zend hashtables are not thread-safe, a mutex is used to prevent two /// interned strings from being created at the same time. /// + /// Interned strings are not used very often. You should almost always use a + /// regular zend string, except in the case that you know you will use a + /// string that PHP will already have interned, such as "PHP". + /// /// # Parameters /// /// * `str` - String content. @@ -146,6 +200,24 @@ impl ZendStr { /// /// * The function used to create interned strings has not been set. /// * The function could not allocate enough memory for the Zend string. + /// + /// # Safety + /// + /// When passing `persistent` as `false`, the caller must ensure that the + /// object does not attempt to live after the request finishes. When a + /// request starts and finishes in PHP, the Zend heap is deallocated and a + /// new one is created, which would leave a dangling pointer in the + /// [`ZBox`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// use std::ffi::CString; + /// + /// let c_s = CString::new("PHP").unwrap(); + /// let s = ZendStr::interned_from_c_str(&c_s, true); + /// ``` pub fn interned_from_c_str(str: &CStr, persistent: bool) -> ZBox { let _lock = INTERNED_LOCK.lock(); @@ -164,11 +236,29 @@ impl ZendStr { } /// Returns the length of the string. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// + /// let s = ZendStr::new("hello, world!", false).unwrap(); + /// assert_eq!(s.len(), 13); + /// ``` pub fn len(&self) -> usize { self.len as usize } /// Returns true if the string is empty, false otherwise. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// + /// let s = ZendStr::new("hello, world!", false).unwrap(); + /// assert_eq!(s.is_empty(), false); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -187,6 +277,16 @@ impl ZendStr { /// /// Returns the [`None`] variant if the [`CStr`] contains non-UTF-8 /// characters. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendStr; + /// + /// let s = ZendStr::new("hello, world!", false).unwrap(); + /// let as_str = s.as_str(); + /// assert_eq!(as_str, Some("hello, world!")); + /// ``` pub fn as_str(&self) -> Option<&str> { self.as_c_str().to_str().ok() } diff --git a/src/types/zval.rs b/src/types/zval.rs index a92b7c0cdb..ffd1cebbe1 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -19,11 +19,20 @@ use crate::{ types::{Callable, HashTable, ZendLong, ZendObject, ZendStr}, }; -/// Zend value. Represents most data types that are in the Zend engine. +/// A zend value. This is the primary storage container used throughout the Zend +/// engine. +/// +/// A zval can be thought of as a Rust enum, a type that can contain different +/// values such as integers, strings, objects etc. pub type Zval = zval; -unsafe impl Send for Zval {} -unsafe impl Sync for Zval {} +// TODO(david): can we make zval send+sync? main problem is that refcounted +// types do not have atomic refcounters, so technically two threads could +// reference the same object and attempt to modify refcounter at the same time. +// need to look into how ZTS works. + +// unsafe impl Send for Zval {} +// unsafe impl Sync for Zval {} impl Zval { /// Creates a new, empty zval. @@ -86,8 +95,10 @@ impl Zval { /// If the zval does not contain a string, the function will check if it /// contains a double or a long, and if so it will convert the value to /// a [`String`] and return it. Don't rely on this logic, as there is - /// potential for this to change to match the output of the [`str()`](# - /// method.str) function. + /// potential for this to change to match the output of the [`str()`] + /// function. + /// + /// [`str()`]: #method.str pub fn string(&self) -> Option { self.str() .map(|s| s.to_string()) diff --git a/src/zend/ce.rs b/src/zend/ce.rs index 73df41c6ce..b2e9d4393f 100644 --- a/src/zend/ce.rs +++ b/src/zend/ce.rs @@ -1,14 +1,21 @@ +//! Stock class entries registered with PHP, primarily exceptions. + #![allow(clippy::unwrap_used)] use crate::ffi::{ zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_compile_error, zend_ce_division_by_zero_error, zend_ce_error_exception, zend_ce_exception, zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error, - zend_ce_value_error, + zend_ce_value_error, zend_standard_class_def, }; use super::ClassEntry; +/// Returns the base `stdClass` class. +pub fn stdclass() -> &'static ClassEntry { + unsafe { zend_standard_class_def.as_ref() }.unwrap() +} + /// Returns the base `Throwable` class. pub fn throwable() -> &'static ClassEntry { unsafe { zend_ce_throwable.as_ref() }.unwrap() diff --git a/src/zend/class.rs b/src/zend/class.rs index 13c13d1f74..7e61c87a5a 100644 --- a/src/zend/class.rs +++ b/src/zend/class.rs @@ -3,15 +3,11 @@ use crate::{ffi::zend_class_entry, flags::ClassFlags, types::ZendStr, zend::ExecutorGlobals}; use std::{convert::TryInto, fmt::Debug, ops::DerefMut}; -/// A Zend class entry. Alias. +/// A PHP class entry. +/// +/// Represents a class registered with the PHP interpreter. pub type ClassEntry = zend_class_entry; -impl PartialEq for ClassEntry { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self, other) - } -} - impl ClassEntry { /// Attempts to find a reference to a class in the global class table. /// @@ -75,6 +71,7 @@ impl ClassEntry { } /// Returns an iterator of all the interfaces that the class implements. + /// /// Returns [`None`] if the interfaces have not been resolved on the /// class. pub fn interfaces(&self) -> Option> { @@ -103,6 +100,12 @@ impl ClassEntry { } } +impl PartialEq for ClassEntry { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self, other) + } +} + impl Debug for ClassEntry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name: String = unsafe { self.name.as_ref() } diff --git a/src/zend/ex.rs b/src/zend/ex.rs index ace8c9ece3..2c433a3877 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -1,6 +1,3 @@ -//! Functions for interacting with the execution data passed to PHP functions\ -//! introduced in Rust. - use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; use crate::{ @@ -9,12 +6,42 @@ use crate::{ types::{ZendClassObject, ZendObject, Zval}, }; -/// Execution data passed when a function is called from Zend. -pub type ExecutionData = zend_execute_data; - -impl ExecutionData { +/// Execute data passed when a function is called from PHP. +/// +/// This generally contains things related to the call, including but not +/// limited to: +/// +/// * Arguments +/// * `$this` object reference +/// * Reference to return value +/// * Previous execute data +pub type ExecuteData = zend_execute_data; + +impl ExecuteData { /// Returns an [`ArgParser`] pre-loaded with the arguments contained inside /// `self`. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType}; + /// + /// #[no_mangle] + /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { + /// let mut a = Arg::new("a", DataType::Long); + /// + /// // The `parse_args!()` macro can be used for this. + /// let parser = ex.parser() + /// .arg(&mut a) + /// .parse(); + /// + /// if parser.is_err() { + /// return; + /// } + /// + /// dbg!(a); + /// } + /// ``` pub fn parser<'a>(&'a mut self) -> ArgParser<'a, '_> { self.parser_object().0 } @@ -24,6 +51,28 @@ impl ExecutionData { /// /// A reference to `$this` is also returned in an [`Option`], which resolves /// to [`None`] if this function is not called inside a method. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType}; + /// + /// #[no_mangle] + /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { + /// let mut a = Arg::new("a", DataType::Long); + /// + /// let (parser, this) = ex.parser_object(); + /// let parser = parser + /// .arg(&mut a) + /// .parse(); + /// + /// if parser.is_err() { + /// return; + /// } + /// + /// dbg!(a, this); + /// } + /// ``` pub fn parser_object<'a>(&'a mut self) -> (ArgParser<'a, '_>, Option<&'a mut ZendObject>) { // SAFETY: All fields of the `u2` union are the same type. let n_args = unsafe { self.This.u2.num_args }; @@ -53,6 +102,37 @@ impl ExecutionData { /// object will also resolve to [`None`] if the function is called /// inside a method that does not belong to an object with type `T`. /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType, prelude::*}; + /// + /// #[php_class] + /// #[derive(Debug)] + /// struct Example; + /// + /// #[no_mangle] + /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { + /// let mut a = Arg::new("a", DataType::Long); + /// + /// let (parser, this) = ex.parser_method::(); + /// let parser = parser + /// .arg(&mut a) + /// .parse(); + /// + /// if parser.is_err() { + /// return; + /// } + /// + /// dbg!(a, this); + /// } + /// + /// #[php_module] + /// pub fn module(module: ModuleBuilder) -> ModuleBuilder { + /// module + /// } + /// ``` + /// /// [`parse_object`]: #method.parse_object pub fn parser_method<'a, T: RegisteredClass>( &'a mut self, @@ -69,14 +149,45 @@ impl ExecutionData { /// /// Returns a [`ZendClassObject`] if the execution data contained a valid /// object of type `T`, otherwise returns [`None`]. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::{types::Zval, zend::ExecuteData, prelude::*}; + /// + /// #[php_class] + /// #[derive(Debug)] + /// struct Example; + /// + /// #[no_mangle] + /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { + /// let this = ex.get_object::(); + /// dbg!(this); + /// } + /// + /// #[php_module] + /// pub fn module(module: ModuleBuilder) -> ModuleBuilder { + /// module + /// } + /// ``` pub fn get_object(&mut self) -> Option<&mut ZendClassObject> { - // TODO(david): This should be a `&mut self` function but we need to fix arg - // parser first. ZendClassObject::from_zend_obj_mut(self.get_self()?) } /// Attempts to retrieve the 'this' object, which can be used in class /// methods to retrieve the underlying Zend object. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::{types::Zval, zend::ExecuteData}; + /// + /// #[no_mangle] + /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { + /// let this = ex.get_self(); + /// dbg!(this); + /// } + /// ``` pub fn get_self(&mut self) -> Option<&mut ZendObject> { // TODO(david): This should be a `&mut self` function but we need to fix arg // parser first. @@ -124,13 +235,13 @@ impl ExecutionData { #[cfg(test)] mod tests { - use super::ExecutionData; + use super::ExecuteData; #[test] fn test_zend_call_frame_slot() { // PHP 8.0.2 (cli) (built: Feb 21 2021 11:51:33) ( NTS ) // Copyright (c) The PHP Group // Zend Engine v4.0.2, Copyright (c) Zend Technologies - assert_eq!(ExecutionData::zend_call_frame_slot(), 5); + assert_eq!(ExecuteData::zend_call_frame_slot(), 5); } } diff --git a/src/zend/function.rs b/src/zend/function.rs index e9d9b08e9b..11c7fcd0c6 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -1,10 +1,10 @@ -//! Builder and objects used to create functions and methods in PHP. +//! Builder for creating functions and methods in PHP. use std::{os::raw::c_char, ptr}; use crate::ffi::zend_function_entry; -/// A Zend function entry. Alias. +/// A Zend function entry. pub type FunctionEntry = zend_function_entry; impl FunctionEntry { diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 3a3b9677f5..7f6ab19535 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -1,5 +1,10 @@ //! Types related to the PHP executor globals. +use std::ops::{Deref, DerefMut}; + +use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +use crate::boxed::ZBox; use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals}; use crate::types::{HashTable, ZendObject}; @@ -8,20 +13,36 @@ use crate::types::{HashTable, ZendObject}; pub type ExecutorGlobals = _zend_executor_globals; impl ExecutorGlobals { - /// Returns a static reference to the PHP executor globals. - pub fn get() -> &'static Self { + /// Returns a 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() -> GlobalReadGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - unsafe { ext_php_rs_executor_globals().as_ref() } - .expect("Static executor globals were invalid") + let globals = unsafe { ext_php_rs_executor_globals().as_ref() } + .expect("Static executor globals were invalid"); + let guard = GLOBALS_LOCK.read(); + GlobalReadGuard { globals, guard } } - fn get_mut() -> &'static mut Self { + /// 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. + fn get_mut() -> GlobalWriteGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - // TODO: Should this be syncronized? - unsafe { ext_php_rs_executor_globals().as_mut() } - .expect("Static executor globals were invalid") + let globals = unsafe { ext_php_rs_executor_globals().as_mut() } + .expect("Static executor globals were invalid"); + let guard = GLOBALS_LOCK.write(); + GlobalWriteGuard { globals, guard } } /// Attempts to retrieve the global class hash table. @@ -30,19 +51,62 @@ impl ExecutorGlobals { } /// Attempts to extract the last PHP exception captured by the interpreter. + /// Returned inside a [`ZBox`]. /// - /// Note that the caller is responsible for freeing the memory here or it'll - /// leak. - pub fn take_exception() -> Option<*mut ZendObject> { - let globals = Self::get_mut(); + /// This function requires the executor globals to be mutably held, which + /// could lead to a deadlock if the globals are already borrowed immutably + /// or mutably. + pub fn take_exception() -> Option> { + let mut globals = Self::get_mut(); let mut exception_ptr = std::ptr::null_mut(); std::mem::swap(&mut exception_ptr, &mut globals.exception); - if !exception_ptr.is_null() { - Some(exception_ptr) - } else { - None - } + // SAFETY: `as_mut` checks for null. + Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) }) + } +} + +/// 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(()); + +/// Wrapper guard that contains a reference to a given type `T`. Dropping a +/// guard releases the lock on the relevant rwlock. +pub struct GlobalReadGuard { + globals: &'static T, + #[allow(dead_code)] + guard: RwLockReadGuard<'static, ()>, +} + +impl Deref for GlobalReadGuard { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.globals + } +} + +/// Wrapper guard that contains a mutable reference to a given type `T`. +/// Dropping a guard releases the lock on the relevant rwlock. +pub struct GlobalWriteGuard { + globals: &'static mut T, + #[allow(dead_code)] + guard: RwLockWriteGuard<'static, ()>, +} + +impl Deref for GlobalWriteGuard { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.globals + } +} + +impl DerefMut for GlobalWriteGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + self.globals } } diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 482ece76ab..1181a0c01b 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -12,6 +12,7 @@ use crate::{ types::{HashTable, ZendClassObject, ZendObject, ZendStr, Zval}, }; +/// A set of functions associated with a PHP class. pub type ZendObjectHandlers = zend_object_handlers; impl ZendObjectHandlers { @@ -206,7 +207,7 @@ impl ZendObjectHandlers { let self_ = &mut **obj; match has_set_exists { - // + // // * 0 (has) whether property exists and is not NULL 0 => { if let Some(val) = prop { @@ -217,7 +218,7 @@ impl ZendObjectHandlers { } } } - // + // // * 1 (set) whether property exists and is true 1 => { if let Some(val) = prop { @@ -229,7 +230,7 @@ impl ZendObjectHandlers { } } } - // + // // * 2 (exists) whether property exists 2 => { if prop.is_some() { diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 4a86683585..3bad6ace76 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -1,3 +1,5 @@ +//! Types used to interact with the Zend engine. + mod _type; pub mod ce; mod class; @@ -9,7 +11,7 @@ mod module; pub use _type::ZendType; pub use class::ClassEntry; -pub use ex::ExecutionData; +pub use ex::ExecuteData; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; pub use handlers::ZendObjectHandlers; diff --git a/src/zend/module.rs b/src/zend/module.rs index 8723deed83..d86d50375a 100644 --- a/src/zend/module.rs +++ b/src/zend/module.rs @@ -3,12 +3,12 @@ use crate::ffi::zend_module_entry; -/// A Zend module entry. Alias. +/// A Zend module entry, also known as an extension. pub type ModuleEntry = zend_module_entry; impl ModuleEntry { - /// Converts the module entry into a raw pointer, releasing it to the C - /// world. + /// Allocates the module entry on the heap, returning a pointer to the + /// memory location. The caller is responsible for the memory pointed to. pub fn into_raw(self) -> *mut Self { Box::into_raw(Box::new(self)) } From c665ad49297ac7342332fa6cad50a1892d71899a Mon Sep 17 00:00:00 2001 From: David Cole Date: Sun, 10 Oct 2021 17:26:21 +1300 Subject: [PATCH 08/10] Add `Zend` prefix to callable and hashtable --- crates/macros/src/extern_.rs | 2 +- guide/src/types/closure.md | 2 +- src/lib.rs | 6 +- src/types/array.rs | 124 ++++++++++++++++++----------------- src/types/callable.rs | 28 ++++---- src/types/mod.rs | 4 +- src/types/zval.rs | 17 +++-- src/zend/globals.rs | 4 +- src/zend/handlers.rs | 8 +-- 9 files changed, 100 insertions(+), 95 deletions(-) diff --git a/crates/macros/src/extern_.rs b/crates/macros/src/extern_.rs index 1db3109cd4..a629e82d01 100644 --- a/crates/macros/src/extern_.rs +++ b/crates/macros/src/extern_.rs @@ -44,7 +44,7 @@ fn parse_function(mut func: ForeignItemFn) -> Result { #(#attrs)* #vis #sig { use ::std::convert::TryInto; - let callable = ::ext_php_rs::types::Callable::try_from_name( + let callable = ::ext_php_rs::types::ZendCallable::try_from_name( #name ).expect(concat!("Unable to find callable function `", #name, "`.")); diff --git a/guide/src/types/closure.md b/guide/src/types/closure.md index caccf16cc3..0cb2902411 100644 --- a/guide/src/types/closure.md +++ b/guide/src/types/closure.md @@ -112,7 +112,7 @@ function by its name, or as a parameter. They can be called through the use ext_php_rs::prelude::*; #[php_function] -pub fn callable_parameter(call: Callable) { +pub fn callable_parameter(call: ZendCallable) { let val = call.try_call(vec![&0, &1, &"Hello"]).expect("Failed to call function"); dbg!(val); } diff --git a/src/lib.rs b/src/lib.rs index 577242ad83..9aee462048 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ pub mod prelude { pub use crate::php_impl; pub use crate::php_module; pub use crate::php_startup; - pub use crate::types::Callable; + pub use crate::types::ZendCallable; pub use crate::ZvalConvert; } @@ -141,7 +141,7 @@ pub use ext_php_rs_derive::php_extern; /// - [`Vec`] and [`HashMap`](std::collections::HashMap) where `T: /// FromZval`. /// - [`Binary`] for passing binary data as a string, where `T: Pack`. -/// - [`Callable`] for receiving PHP callables, not applicable for return +/// - [`ZendCallable`] for receiving PHP callables, not applicable for return /// values. /// - [`Option`] where `T: FromZval`. When used as a parameter, the parameter /// will be @@ -273,7 +273,7 @@ pub use ext_php_rs_derive::php_extern; /// [`IntoZval`]: crate::php::types::zval::IntoZval /// [`Zval`]: crate::php::types::zval::Zval /// [`Binary`]: crate::php::types::binary::Binary -/// [`Callable`]: crate::php::types::callable::Callable +/// [`ZendCallable`]: crate::php::types::callable::ZendCallable /// [`PhpException`]: crate::php::exceptions::PhpException pub use ext_php_rs_derive::php_function; diff --git a/src/types/array.rs b/src/types/array.rs index de028ef270..8417035406 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -38,9 +38,9 @@ use crate::{ /// # Examples /// /// ```no_run -/// use ext_php_rs::types::HashTable; +/// use ext_php_rs::types::ZendHashTable; /// -/// let mut ht = HashTable::new(); +/// let mut ht = ZendHashTable::new(); /// ht.push(1); /// ht.push("Hello, world!"); /// ht.insert("Like", "Hashtable"); @@ -48,20 +48,20 @@ use crate::{ /// assert_eq!(ht.len(), 3); /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(1)); /// ``` -pub type HashTable = crate::ffi::HashTable; +pub type ZendHashTable = crate::ffi::HashTable; // Clippy complains about there being no `is_empty` function when implementing // on the alias `ZendStr` :( #[allow(clippy::len_without_is_empty)] -impl HashTable { +impl ZendHashTable { /// Creates a new, empty, PHP hashtable, returned inside a [`ZBox`]. /// /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let ht = HashTable::new(); + /// let ht = ZendHashTable::new(); /// ``` /// /// # Panics @@ -81,9 +81,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let ht = HashTable::with_capacity(10); + /// let ht = ZendHashTable::with_capacity(10); /// ``` /// /// # Panics @@ -107,9 +107,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.push(1); /// ht.push("Hello, world"); @@ -125,9 +125,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// assert_eq!(ht.is_empty(), true); /// @@ -145,9 +145,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.insert("test", "hello world"); /// assert_eq!(ht.is_empty(), false); @@ -174,9 +174,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.insert("test", "hello world"); /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world")); @@ -201,9 +201,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.push(100); /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100)); @@ -226,9 +226,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.insert("test", "hello world"); /// assert_eq!(ht.len(), 1); @@ -261,9 +261,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.push("hello"); /// assert_eq!(ht.len(), 1); @@ -298,9 +298,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.insert("a", "A"); /// ht.insert("b", "B"); @@ -340,9 +340,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.insert_at_index(0, "A"); /// ht.insert_at_index(5, "B"); @@ -374,9 +374,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// ht.push("a"); /// ht.push("b"); @@ -400,9 +400,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// for (idx, key, val) in ht.iter() { /// // ^ Index if inserted at an index. @@ -422,9 +422,9 @@ impl HashTable { /// # Example /// /// ```no_run - /// use ext_php_rs::types::HashTable; + /// use ext_php_rs::types::ZendHashTable; /// - /// let mut ht = HashTable::new(); + /// let mut ht = ZendHashTable::new(); /// /// for val in ht.values() { /// dbg!(val); @@ -435,14 +435,14 @@ impl HashTable { } } -unsafe impl ZBoxable for HashTable { +unsafe impl ZBoxable for ZendHashTable { fn free(&mut self) { // SAFETY: ZBox has immutable access to `self`. unsafe { zend_array_destroy(self) } } } -impl Debug for HashTable { +impl Debug for ZendHashTable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_map() .entries( @@ -453,13 +453,13 @@ impl Debug for HashTable { } } -impl ToOwned for HashTable { - type Owned = ZBox; +impl ToOwned for ZendHashTable { + type Owned = ZBox; fn to_owned(&self) -> Self::Owned { unsafe { // SAFETY: FFI call does not modify `self`, returns a new hashtable. - let ptr = zend_array_dup(self as *const HashTable as *mut HashTable); + let ptr = zend_array_dup(self as *const ZendHashTable as *mut ZendHashTable); // SAFETY: `as_mut()` checks if the pointer is null, and panics if it is not. ZBox::from_raw( @@ -472,7 +472,7 @@ impl ToOwned for HashTable { /// Immutable iterator upon a reference to a hashtable. pub struct Iter<'a> { - ht: &'a HashTable, + ht: &'a ZendHashTable, pos: Option>, end: Option>, } @@ -483,7 +483,7 @@ impl<'a> Iter<'a> { /// # Parameters /// /// * `ht` - The hashtable to iterate. - pub fn new(ht: &'a HashTable) -> Self { + pub fn new(ht: &'a ZendHashTable) -> Self { Self { ht, pos: NonNull::new(ht.arData), @@ -551,7 +551,7 @@ impl<'a> Values<'a> { /// # Parameters /// /// * `ht` - The hashtable to iterate. - pub fn new(ht: &'a HashTable) -> Self { + pub fn new(ht: &'a ZendHashTable) -> Self { Self(Iter::new(ht)) } } @@ -583,19 +583,19 @@ impl<'a> DoubleEndedIterator for Values<'a> { } } -impl Default for ZBox { +impl Default for ZBox { fn default() -> Self { - HashTable::new() + ZendHashTable::new() } } -impl Clone for ZBox { +impl Clone for ZBox { fn clone(&self) -> Self { (**self).to_owned() } } -impl IntoZval for ZBox { +impl IntoZval for ZBox { const TYPE: DataType = DataType::Array; fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> { @@ -604,7 +604,7 @@ impl IntoZval for ZBox { } } -impl<'a> FromZval<'a> for &'a HashTable { +impl<'a> FromZval<'a> for &'a ZendHashTable { const TYPE: DataType = DataType::Array; fn from_zval(zval: &'a Zval) -> Option { @@ -616,13 +616,13 @@ impl<'a> FromZval<'a> for &'a HashTable { //// HashMap /////////////////////////////////////////// -impl TryFrom<&HashTable> for HashMap +impl TryFrom<&ZendHashTable> for HashMap where for<'a> V: FromZval<'a>, { type Error = Error; - fn try_from(value: &HashTable) -> Result { + fn try_from(value: &ZendHashTable) -> Result { let mut hm = HashMap::with_capacity(value.len()); for (idx, key, val) in value.iter() { @@ -636,7 +636,7 @@ where } } -impl TryFrom> for ZBox +impl TryFrom> for ZBox where K: AsRef, V: IntoZval, @@ -644,8 +644,9 @@ where type Error = Error; fn try_from(value: HashMap) -> Result { - let mut ht = - HashTable::with_capacity(value.len().try_into().map_err(|_| Error::IntegerOverflow)?); + let mut ht = ZendHashTable::with_capacity( + value.len().try_into().map_err(|_| Error::IntegerOverflow)?, + ); for (k, v) in value.into_iter() { ht.insert(k.as_ref(), v)?; @@ -684,13 +685,13 @@ where //// Vec /////////////////////////////////////////// -impl TryFrom<&HashTable> for Vec +impl TryFrom<&ZendHashTable> for Vec where for<'a> T: FromZval<'a>, { type Error = Error; - fn try_from(value: &HashTable) -> Result { + fn try_from(value: &ZendHashTable) -> Result { let mut vec = Vec::with_capacity(value.len()); for (_, _, val) in value.iter() { @@ -701,15 +702,16 @@ where } } -impl TryFrom> for ZBox +impl TryFrom> for ZBox where T: IntoZval, { type Error = Error; fn try_from(value: Vec) -> Result { - let mut ht = - HashTable::with_capacity(value.len().try_into().map_err(|_| Error::IntegerOverflow)?); + let mut ht = ZendHashTable::with_capacity( + value.len().try_into().map_err(|_| Error::IntegerOverflow)?, + ); for val in value.into_iter() { ht.push(val)?; @@ -743,9 +745,9 @@ where } } -impl FromIterator for ZBox { +impl FromIterator for ZBox { fn from_iter>(iter: T) -> Self { - let mut ht = HashTable::new(); + let mut ht = ZendHashTable::new(); for item in iter.into_iter() { // Inserting a zval cannot fail, as `push` only returns `Err` if converting // `val` to a zval fails. @@ -755,9 +757,9 @@ impl FromIterator for ZBox { } } -impl FromIterator<(u64, Zval)> for ZBox { +impl FromIterator<(u64, Zval)> for ZBox { fn from_iter>(iter: T) -> Self { - let mut ht = HashTable::new(); + let mut ht = ZendHashTable::new(); for (key, val) in iter.into_iter() { // Inserting a zval cannot fail, as `push` only returns `Err` if converting // `val` to a zval fails. @@ -767,9 +769,9 @@ impl FromIterator<(u64, Zval)> for ZBox { } } -impl<'a> FromIterator<(&'a str, Zval)> for ZBox { +impl<'a> FromIterator<(&'a str, Zval)> for ZBox { fn from_iter>(iter: T) -> Self { - let mut ht = HashTable::new(); + let mut ht = ZendHashTable::new(); for (key, val) in iter.into_iter() { // Inserting a zval cannot fail, as `push` only returns `Err` if converting // `val` to a zval fails. diff --git a/src/types/callable.rs b/src/types/callable.rs index 15088db1e4..89e33149bd 100644 --- a/src/types/callable.rs +++ b/src/types/callable.rs @@ -17,10 +17,10 @@ use super::Zval; /// /// [`try_call`]: #method.try_call #[derive(Debug)] -pub struct Callable<'a>(OwnedZval<'a>); +pub struct ZendCallable<'a>(OwnedZval<'a>); -impl<'a> Callable<'a> { - /// Attempts to create a new [`Callable`] from a zval. +impl<'a> ZendCallable<'a> { + /// Attempts to create a new [`ZendCallable`] from a zval. /// /// # Parameters /// @@ -37,7 +37,7 @@ impl<'a> Callable<'a> { } } - /// Attempts to create a new [`Callable`] by taking ownership of a Zval. + /// Attempts to create a new [`ZendCallable`] by taking ownership of a Zval. /// Returns a result containing the callable if the zval was callable. /// /// # Parameters @@ -51,8 +51,8 @@ impl<'a> Callable<'a> { } } - /// Attempts to create a new [`Callable`] from a function name. Returns a - /// result containing the callable if the function existed and was + /// Attempts to create a new [`ZendCallable`] from a function name. Returns + /// a result containing the callable if the function existed and was /// callable. /// /// # Parameters @@ -62,9 +62,9 @@ impl<'a> Callable<'a> { /// # Example /// /// ```no_run - /// use ext_php_rs::types::Callable; + /// use ext_php_rs::types::ZendCallable; /// - /// let strpos = Callable::try_from_name("strpos").unwrap(); + /// let strpos = ZendCallable::try_from_name("strpos").unwrap(); /// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap(); /// assert_eq!(result.long(), Some(1)); /// ``` @@ -93,9 +93,9 @@ impl<'a> Callable<'a> { /// # Example /// /// ```no_run - /// use ext_php_rs::types::Callable; + /// use ext_php_rs::types::ZendCallable; /// - /// let strpos = Callable::try_from_name("strpos").unwrap(); + /// let strpos = ZendCallable::try_from_name("strpos").unwrap(); /// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap(); /// assert_eq!(result.long(), Some(1)); /// ``` @@ -133,19 +133,19 @@ impl<'a> Callable<'a> { } } -impl<'a> FromZval<'a> for Callable<'a> { +impl<'a> FromZval<'a> for ZendCallable<'a> { const TYPE: DataType = DataType::Callable; fn from_zval(zval: &'a Zval) -> Option { - Callable::new(zval).ok() + ZendCallable::new(zval).ok() } } -impl<'a> TryFrom for Callable<'a> { +impl<'a> TryFrom for ZendCallable<'a> { type Error = Error; fn try_from(value: Zval) -> Result { - Callable::new_owned(value) + ZendCallable::new_owned(value) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 80bf8666ec..888e056e78 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -11,8 +11,8 @@ mod object; mod string; mod zval; -pub use array::HashTable; -pub use callable::Callable; +pub use array::ZendHashTable; +pub use callable::ZendCallable; pub use class_object::ZendClassObject; pub use long::ZendLong; pub use object::{PropertyQuery, ZendObject}; diff --git a/src/types/zval.rs b/src/types/zval.rs index ffd1cebbe1..0da658fba6 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -16,7 +16,7 @@ use crate::{ flags::DataType, flags::ZvalTypeFlags, rc::PhpRc, - types::{Callable, HashTable, ZendLong, ZendObject, ZendStr}, + types::{ZendCallable, ZendHashTable, ZendLong, ZendObject, ZendStr}, }; /// A zend value. This is the primary storage container used throughout the Zend @@ -150,7 +150,7 @@ impl Zval { /// Returns an immutable reference to the underlying zval hashtable if the /// zval contains an array. - pub fn array(&self) -> Option<&HashTable> { + pub fn array(&self) -> Option<&ZendHashTable> { if self.is_array() { unsafe { self.value.arr.as_ref() } } else { @@ -160,7 +160,7 @@ impl Zval { /// Returns a mutable reference to the underlying zval hashtable if the zval /// contains an array. - pub fn array_mut(&mut self) -> Option<&mut HashTable> { + pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> { if self.is_array() { unsafe { self.value.arr.as_mut() } } else { @@ -206,9 +206,9 @@ impl Zval { } /// Returns the value of the zval if it is callable. - pub fn callable(&self) -> Option { + pub fn callable(&self) -> Option { // The Zval is checked if it is callable in the `new` function. - Callable::new(self).ok() + ZendCallable::new(self).ok() } /// Returns the value of the zval if it is a pointer. @@ -438,7 +438,10 @@ impl Zval { /// # Parameters /// /// * `val` - The value to set the zval as. - pub fn set_array, Error = Error>>(&mut self, val: T) -> Result<()> { + pub fn set_array, Error = Error>>( + &mut self, + val: T, + ) -> Result<()> { self.set_hashtable(val.try_into()?); Ok(()) } @@ -449,7 +452,7 @@ impl Zval { /// # Parameters /// /// * `val` - The value to set the zval as. - pub fn set_hashtable(&mut self, val: ZBox) { + pub fn set_hashtable(&mut self, val: ZBox) { self.change_type(ZvalTypeFlags::ArrayEx); self.value.arr = val.into_raw(); } diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 7f6ab19535..6a171180c8 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -7,7 +7,7 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals}; -use crate::types::{HashTable, ZendObject}; +use crate::types::{ZendHashTable, ZendObject}; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; @@ -46,7 +46,7 @@ impl ExecutorGlobals { } /// Attempts to retrieve the global class hash table. - pub fn class_table(&self) -> Option<&HashTable> { + pub fn class_table(&self) -> Option<&ZendHashTable> { unsafe { self.class_table.as_ref() } } diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 1181a0c01b..fede613937 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -9,7 +9,7 @@ use crate::{ zend_std_write_property, }, flags::ZvalTypeFlags, - types::{HashTable, ZendClassObject, ZendObject, ZendStr, Zval}, + types::{ZendClassObject, ZendHashTable, ZendObject, ZendStr, Zval}, }; /// A set of functions associated with a PHP class. @@ -144,11 +144,11 @@ impl ZendObjectHandlers { unsafe extern "C" fn get_properties( object: *mut ZendObject, - ) -> *mut HashTable { + ) -> *mut ZendHashTable { #[inline(always)] unsafe fn internal( object: *mut ZendObject, - props: &mut HashTable, + props: &mut ZendHashTable, ) -> PhpResult { let obj = object .as_mut() @@ -172,7 +172,7 @@ impl ZendObjectHandlers { let props = zend_std_get_properties(object) .as_mut() - .or_else(|| Some(HashTable::new().into_raw())) + .or_else(|| Some(ZendHashTable::new().into_raw())) .expect("Failed to get property hashtable"); if let Err(e) = internal::(object, props) { From aa60bd0ba5609c98f0a96b1ed18b88766ef70285 Mon Sep 17 00:00:00 2001 From: David Cole Date: Sun, 10 Oct 2021 17:35:25 +1300 Subject: [PATCH 09/10] Updated guide types --- guide/src/types/binary.md | 6 +++--- guide/src/types/bool.md | 6 +++--- guide/src/types/closure.md | 6 +++--- guide/src/types/hashmap.md | 6 +++--- guide/src/types/numbers.md | 6 +++--- guide/src/types/option.md | 6 +++--- guide/src/types/str.md | 6 +++--- guide/src/types/string.md | 8 ++++---- guide/src/types/vec.md | 6 +++--- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/guide/src/types/binary.md b/guide/src/types/binary.md index f342ccdc0a..ea6d72548f 100644 --- a/guide/src/types/binary.md +++ b/guide/src/types/binary.md @@ -4,9 +4,9 @@ Binary data is represented as a string in PHP. The most common source of this data is from the [`pack`] and [`unpack`] functions. It allows you to transfer arbitrary binary data between Rust and PHP. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------ | -| Yes | No | Yes | `zend_string` | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------ | +| Yes | No | Yes | No | `zend_string` | The binary type is represented as a string in PHP. Although not encoded, the data is converted into an array and then the pointer to the data is set as the diff --git a/guide/src/types/bool.md b/guide/src/types/bool.md index 18b55eb974..11a8c797a9 100644 --- a/guide/src/types/bool.md +++ b/guide/src/types/bool.md @@ -2,9 +2,9 @@ A boolean. Not much else to say here. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------ | -| Yes | No | Yes | Union flag | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------ | +| Yes | No | Yes | No | Union flag | Booleans are not actually stored inside the zval. Instead, they are treated as two different union types (the zval can be in a true or false state). An diff --git a/guide/src/types/closure.md b/guide/src/types/closure.md index 0cb2902411..9a41a4b842 100644 --- a/guide/src/types/closure.md +++ b/guide/src/types/closure.md @@ -16,9 +16,9 @@ PHP callables (which includes closures) can be passed to Rust through the `Callable` type. When calling a callable, you must provide it with a `Vec` of arguemnts, all of which must implement `IntoZval` and `Clone`. -| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | -| ------------- | -------------- | ----------------------------------- | ---------------- | ------------------------------------------------------------------------------------------ | -| `Callable` | No | `Closure`, `Callable` for functions | No | Callables are implemented in PHP, closures are represented as an instance of `PhpClosure`. | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | -------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------ | +| `Callable` | No | `Closure`, `Callable`for PHP functions | No | Callables are implemented in PHP, closures are represented as an instance of `PhpClosure`. | Internally, when you enable the `closure` feature, a class `PhpClosure` is registered alongside your other classes: diff --git a/guide/src/types/hashmap.md b/guide/src/types/hashmap.md index 3b5721e6da..ddc06b0147 100644 --- a/guide/src/types/hashmap.md +++ b/guide/src/types/hashmap.md @@ -2,9 +2,9 @@ `HashMap`s are represented as associative arrays in PHP. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------ | -| Yes | No | Yes | `HashTable` | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------ | +| Yes | No | Yes | No | `ZendHashTable` | Converting from a zval to a `HashMap` is valid when the key is a `String`, and the value implements `FromZval`. The key and values are copied into Rust types diff --git a/guide/src/types/numbers.md b/guide/src/types/numbers.md index 2d83c6b8fd..b50636ad6c 100644 --- a/guide/src/types/numbers.md +++ b/guide/src/types/numbers.md @@ -3,9 +3,9 @@ Primitive integers include `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `isize`, `usize`, `f32` and `f64`. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | -------------------------------------------------------------------------------- | -| Yes | No | Yes | `i32` on 32-bit platforms, `i64` on 64-bit platforms, `f64` platform-independent | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | -------------------------------------------------------------------------------- | +| Yes | No | Yes | No | `i32` on 32-bit platforms, `i64` on 64-bit platforms, `f64` platform-independent | Note that internally, PHP treats **all** of these integers the same (a 'long'), and therefore it must be converted into a long to be stored inside the zval. A diff --git a/guide/src/types/option.md b/guide/src/types/option.md index d1ae0d0e85..e2391d121e 100644 --- a/guide/src/types/option.md +++ b/guide/src/types/option.md @@ -4,9 +4,9 @@ Options are used for optional and nullable parameters, as well as null returns. It is valid to be converted to/from a zval as long as the underlying `T` generic is also able to be converted to/from a zval. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------ | -| Yes | No | Yes | Depends on `T` | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ---------------------------------- | +| Yes | No | Yes | No | Depends on `T`, `null` for `None`. | Using `Option` as a parameter indicates that the parameter is nullable. If null is passed, a `None` value will be supplied. It is also used in the place of diff --git a/guide/src/types/str.md b/guide/src/types/str.md index d30be3f122..08759291ba 100644 --- a/guide/src/types/str.md +++ b/guide/src/types/str.md @@ -4,9 +4,9 @@ A borrowed string. When this type is encountered, you are given a reference to the actual zend string memory, rather than copying the contents like if you were taking an owned `String` argument. -| `T` parameter | `&T` parameter | `&T` Return type | PHP representation | -| ------------- | -------------- | ---------------- | ------------------------ | -| No | Yes | Yes | `zend_string` (C-string) | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------------ | +| No | Yes | No | Yes | `zend_string` (C-string) | Note that you cannot expect the function to operate the same by swapping out `String` and `&str` - since the zend string memory is read directly, this diff --git a/guide/src/types/string.md b/guide/src/types/string.md index db44f9bb94..fa7fd137b7 100644 --- a/guide/src/types/string.md +++ b/guide/src/types/string.md @@ -4,9 +4,9 @@ When a `String` type is encountered, the zend string content is copied to/from a Rust `String` object. If the zval does not contain a string, it will attempt to read a `double` from the zval and convert it into a `String` object. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------------ | -| Yes | No | Yes | `zend_string` (C-string) | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------------ | +| Yes | No | Yes | No | `zend_string` (C-string) | Internally, PHP stores strings in `zend_string` objects, which is a refcounted C struct containing the string length with the content of the string appended to @@ -20,7 +20,7 @@ be thrown if one is encountered while converting a `String` to a zval. # extern crate ext_php_rs; # use ext_php_rs::prelude::*; #[php_function] -pub fn str_example(input: &str) -> String { +pub fn str_example(input: String) -> String { format!("Hello {}", input) } ``` diff --git a/guide/src/types/vec.md b/guide/src/types/vec.md index 3907e157db..03b5b81173 100644 --- a/guide/src/types/vec.md +++ b/guide/src/types/vec.md @@ -4,9 +4,9 @@ Vectors can contain any type that can be represented as a zval. Note that the data contained in the array will be copied into Rust types and stored inside the vector. The internal representation of a PHP array is discussed below. -| `T` parameter | `&T` parameter | `T` Return type | PHP representation | -| ------------- | -------------- | --------------- | ------------------ | -| Yes | No | Yes | `HashTable` | +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +| ------------- | -------------- | --------------- | ---------------- | ------------------ | +| Yes | No | Yes | No | `ZendHashTable` | Internally, PHP arrays are hash tables where the key can be an unsigned long or a string. Zvals are contained inside arrays therefore the data does not have to From ab88f37867a3ebfc7a3a26537e25830ab7716a17 Mon Sep 17 00:00:00 2001 From: David Cole Date: Sun, 10 Oct 2021 17:47:21 +1300 Subject: [PATCH 10/10] Updated changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 967e76eba1..038ddeec6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Version 0.6.0 + +- Reorganized project. [#101] + - Changed (almost all) module paths. Too many changes to list them all, check + out the docs. + - Removed `skel` project. + +[#101]: https://github.com/davidcole1340/ext-php-rs/pull/101 + ## Version 0.5.3 - Fixed docs.rs PHP bindings file.