use bitflags::bitflags;
#[cfg(php81)]
use crate::ffi::ZEND_ACC_ENUM;
#[cfg(not(php82))]
use crate::ffi::ZEND_ACC_REUSE_GET_ITERATOR;
use crate::ffi::{
_IS_BOOL, CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR,
E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE,
E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING,
E_WARNING, GC_IMMUTABLE, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE,
IS_INDIRECT, IS_ITERABLE, 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,
PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, Z_TYPE_FLAGS_SHIFT,
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_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_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES,
ZEND_ACC_RESOLVED_PARENT, ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES,
ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE,
ZEND_ACC_USE_GUARDS, ZEND_ACC_USES_THIS, ZEND_ACC_VARIADIC, ZEND_EVAL_CODE,
ZEND_HAS_STATIC_IN_METHODS, ZEND_INTERNAL_FUNCTION, ZEND_USER_FUNCTION,
};
use std::{convert::TryFrom, fmt::Display};
use crate::error::{Error, Result};
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
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 Iterable = IS_ITERABLE;
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);
/// Collectable
const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT);
/// Immutable (used for the shared empty array)
const Immutable = GC_IMMUTABLE;
}
}
bitflags! {
/// Flags for building classes.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct ClassFlags: u32 {
/// Final class or method
const Final = ZEND_ACC_FINAL;
/// Abstract method
const Abstract = ZEND_ACC_ABSTRACT;
/// Immutable `op_array` and class_entries
/// (implemented only for lazy loading of `op_array`s)
const Immutable = ZEND_ACC_IMMUTABLE;
/// Function has typed arguments / class has typed props
const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS;
/// Top-level class or function declaration
const TopLevel = ZEND_ACC_TOP_LEVEL;
/// op_array or class is preloaded
const Preloaded = ZEND_ACC_PRELOADED;
/// Class entry is an interface
const Interface = ZEND_ACC_INTERFACE;
/// Class entry is a trait
const Trait = ZEND_ACC_TRAIT;
/// Anonymous class
const AnonymousClass = ZEND_ACC_ANON_CLASS;
/// Class is an Enum
#[cfg(php81)]
const Enum = ZEND_ACC_ENUM;
/// Class linked with parent, interfaces and traits
const Linked = ZEND_ACC_LINKED;
/// Class is abstract, since it is set by any abstract method
const ImplicitAbstractClass = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
/// Class has magic methods `__get`/`__set`/`__unset`/`__isset` that use guards
const UseGuards = ZEND_ACC_USE_GUARDS;
/// Class constants updated
const ConstantsUpdated = ZEND_ACC_CONSTANTS_UPDATED;
/// Objects of this class may not have dynamic properties
const NoDynamicProperties = ZEND_ACC_NO_DYNAMIC_PROPERTIES;
/// User class has methods with static variables
const HasStaticInMethods = ZEND_HAS_STATIC_IN_METHODS;
/// Children must reuse parent `get_iterator()`
#[cfg(not(php82))]
const ReuseGetIterator = ZEND_ACC_REUSE_GET_ITERATOR;
/// Parent class is resolved (CE)
const ResolvedParent = ZEND_ACC_RESOLVED_PARENT;
/// Interfaces are resolved (CE)
const ResolvedInterfaces = ZEND_ACC_RESOLVED_INTERFACES;
/// Class has unresolved variance obligations
const UnresolvedVariance = ZEND_ACC_UNRESOLVED_VARIANCE;
/// Class is linked apart from variance obligations
const NearlyLinked = ZEND_ACC_NEARLY_LINKED;
/// Class cannot be serialized or unserialized
#[cfg(php81)]
const NotSerializable = crate::ffi::ZEND_ACC_NOT_SERIALIZABLE;
/// Readonly class (PHP 8.2+)
/// All properties are implicitly readonly
#[cfg(php82)]
const ReadonlyClass = crate::ffi::ZEND_ACC_READONLY_CLASS;
}
}
bitflags! {
/// Flags for building methods.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct MethodFlags: u32 {
/// Visibility public
const Public = ZEND_ACC_PUBLIC;
/// Visibility protected
const Protected = ZEND_ACC_PROTECTED;
/// Visibility private
const Private = ZEND_ACC_PRIVATE;
/// Method or property overrides private one
const Changed = ZEND_ACC_CHANGED;
/// Static method
const Static = ZEND_ACC_STATIC;
/// Final method
const Final = ZEND_ACC_FINAL;
/// Abstract method
const Abstract = ZEND_ACC_ABSTRACT;
/// Immutable `op_array` and class_entries
/// (implemented only for lazy loading of op_arrays)
const Immutable = ZEND_ACC_IMMUTABLE;
/// Function has typed arguments / class has typed props
const HasTypeHints = ZEND_ACC_HAS_TYPE_HINTS;
/// Top-level class or function declaration
const TopLevel = ZEND_ACC_TOP_LEVEL;
/// `op_array` or class is preloaded
const Preloaded = ZEND_ACC_PRELOADED;
/// Deprecation flag
const Deprecated = ZEND_ACC_DEPRECATED;
/// Function returning by reference
const ReturnReference = ZEND_ACC_RETURN_REFERENCE;
/// Function has a return type
const HasReturnType = ZEND_ACC_HAS_RETURN_TYPE;
/// Function with variable number of arguments
const Variadic = ZEND_ACC_VARIADIC;
/// `op_array` has finally blocks (user only)
const HasFinallyBlock = ZEND_ACC_HAS_FINALLY_BLOCK;
/// "main" `op_array` with `ZEND_DECLARE_CLASS_DELAYED` opcodes
const EarlyBinding = ZEND_ACC_EARLY_BINDING;
/// Closure uses `$this`
const UsesThis = ZEND_ACC_USES_THIS;
/// Call through user function trampoline
///
/// # Example
/// - `__call`
/// - `__callStatic`
const CallViaTrampoline = ZEND_ACC_CALL_VIA_TRAMPOLINE;
/// Disable inline caching
const NeverCache = ZEND_ACC_NEVER_CACHE;
/// `op_array` is a clone of trait method
const TraitClone = ZEND_ACC_TRAIT_CLONE;
/// Function is a constructor
const IsConstructor = ZEND_ACC_CTOR;
/// Function is a closure
const Closure = ZEND_ACC_CLOSURE;
/// Function is a fake closure
const FakeClosure = ZEND_ACC_FAKE_CLOSURE;
/// Function is a generator
const Generator = ZEND_ACC_GENERATOR;
/// Function was processed by pass two (user only)
const DonePassTwo = ZEND_ACC_DONE_PASS_TWO;
/// `run_time_cache` allocated on heap (user only)
const HeapRTCache = ZEND_ACC_HEAP_RT_CACHE;
/// `op_array` uses strict mode types
const StrictTypes = ZEND_ACC_STRICT_TYPES;
}
}
bitflags! {
/// Flags for building properties.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct PropertyFlags: u32 {
/// Visibility public
const Public = ZEND_ACC_PUBLIC;
/// Visibility protected
const Protected = ZEND_ACC_PROTECTED;
/// Visibility private
const Private = ZEND_ACC_PRIVATE;
/// Property or method overrides private one
const Changed = ZEND_ACC_CHANGED;
/// Static property
const Static = ZEND_ACC_STATIC;
/// Promoted property
const Promoted = ZEND_ACC_PROMOTED;
}
}
bitflags! {
/// Flags for building constants.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct ConstantFlags: u32 {
/// Visibility public
const Public = ZEND_ACC_PUBLIC;
/// Visibility protected
const Protected = ZEND_ACC_PROTECTED;
/// Visibility private
const Private = ZEND_ACC_PRIVATE;
/// Promoted constant
const Promoted = ZEND_ACC_PROMOTED;
}
}
bitflags! {
/// Flags for building module global constants.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct GlobalConstantFlags: u32 {
/// No longer used -- always case-sensitive
#[deprecated(note = "No longer used -- always case-sensitive")]
const CaseSensitive = CONST_CS;
/// Persistent
const Persistent = CONST_PERSISTENT;
/// Can't be saved in file cache
const NoFileCache = CONST_NO_FILE_CACHE;
/// Deprecated (this flag is not deprecated, it literally means the constant is deprecated)
const Deprecated = CONST_DEPRECATED;
}
}
bitflags! {
/// Represents the result of a function.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct ZendResult: i32 {
/// Function call was successful.
const Success = 0;
/// Function call failed.
const Failure = -1;
}
}
bitflags! {
/// Represents permissions for where a configuration setting may be set.
pub struct IniEntryPermission: u32 {
/// User
const User = PHP_INI_USER;
/// Per directory
const PerDir = PHP_INI_PERDIR;
/// System
const System = PHP_INI_SYSTEM;
/// All
const All = PHP_INI_ALL;
}
}
bitflags! {
/// Represents error types when used via php_error_docref for example.
pub struct ErrorType: u32 {
/// Error
const Error = E_ERROR;
/// Warning
const Warning = E_WARNING;
/// Parse
const Parse = E_PARSE;
/// Notice
const Notice = E_NOTICE;
/// Core error
const CoreError = E_CORE_ERROR;
/// Core warning
const CoreWarning = E_CORE_WARNING;
/// Compile error
const CompileError = E_COMPILE_ERROR;
/// Compile warning
const CompileWarning = E_COMPILE_WARNING;
/// User error
#[cfg_attr(php84, deprecated = "`E_USER_ERROR` is deprecated since PHP 8.4. Throw an exception instead.")]
const UserError = E_USER_ERROR;
/// User warning
const UserWarning = E_USER_WARNING;
/// User notice
const UserNotice = E_USER_NOTICE;
/// Strict
const Strict = E_STRICT;
/// Recoverable error
const RecoverableError = E_RECOVERABLE_ERROR;
/// Deprecated
const Deprecated = E_DEPRECATED;
/// User deprecated
const UserDeprecated = E_USER_DEPRECATED;
}
}
/// Represents the type of a function.
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub enum FunctionType {
/// Internal function
Internal,
/// User function
User,
/// Eval code
Eval,
}
impl From<u8> for FunctionType {
#[allow(clippy::bad_bit_mask)]
fn from(value: u8) -> Self {
match value.into() {
ZEND_INTERNAL_FUNCTION => Self::Internal,
ZEND_USER_FUNCTION => Self::User,
ZEND_EVAL_CODE => Self::Eval,
_ => panic!("Unknown function type: {value}"),
}
}
}
/// Valid data types for PHP.
#[repr(C, u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum DataType {
/// Undefined
Undef,
/// `null`
Null,
/// `false`
False,
/// `true`
True,
/// Integer (the irony)
Long,
/// Floating point number
Double,
/// String
String,
/// Array
Array,
/// Iterable
Iterable,
/// Object
Object(Option<&'static str>),
/// Resource
Resource,
/// Reference
Reference,
/// Callable
Callable,
/// Constant expression
ConstantExpression,
/// Void
#[default]
Void,
/// Mixed
Mixed,
/// Boolean
Bool,
/// Pointer
Ptr,
/// Indirect (internal)
Indirect,
}
impl DataType {
/// Returns the integer representation of the data type.
#[must_use]
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 | DataType::Reference => IS_RESOURCE,
DataType::Indirect => IS_INDIRECT,
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,
DataType::Iterable => IS_ITERABLE,
}
}
}
// 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<u32> for Type { ... }
impl TryFrom<ZvalTypeFlags> for DataType {
type Error = Error;
fn try_from(value: ZvalTypeFlags) -> Result<Self> {
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<u32> 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_PTR, Ptr);
contains!(IS_INDIRECT, Indirect);
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);
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"),
DataType::Indirect => write!(f, "Indirect"),
DataType::Iterable => write!(f, "Iterable"),
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unnecessary_fallible_conversions)]
use super::DataType;
use crate::ffi::{
IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE,
IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR,
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_INDIRECT, Indirect);
test!(IS_VOID, Void);
test!(IS_PTR, Ptr);
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);
}
}