Type Definition ext_php_rs::types::Zval
source · pub type Zval = zval;
Expand description
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.
Implementations§
source§impl Zval
impl Zval
sourcepub const fn new() -> Self
pub const fn new() -> Self
Creates a new, empty zval.
Examples found in repository?
More examples
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
pub fn try_from_name(name: &str) -> Result<Self> {
let mut callable = Zval::new();
callable.set_string(name, false)?;
Self::new_owned(callable)
}
/// Attempts to call the callable with a list of arguments to pass to the
/// function.
///
/// You should not call this function directly, rather through the
/// [`call_user_func`] macro.
///
/// # 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::ZendCallable;
///
/// let strpos = ZendCallable::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<Zval> {
if !self.0.is_callable() {
return Err(Error::Callable);
}
let mut retval = Zval::new();
let len = params.len();
let params = params
.into_iter()
.map(|val| val.as_zval(false))
.collect::<Result<Vec<_>>>()?;
let packed = params.into_boxed_slice();
let result = unsafe {
_call_user_function_impl(
std::ptr::null_mut(),
self.0.as_ref() as *const crate::ffi::_zval_struct as *mut crate::ffi::_zval_struct,
&mut retval,
len as _,
packed.as_ptr() as *mut _,
std::ptr::null_mut(),
)
};
if result < 0 {
Err(Error::Callable)
} else if let Some(e) = ExecutorGlobals::take_exception() {
Err(Error::Exception(e))
} else {
Ok(retval)
}
}
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
pub fn shallow_clone(&self) -> Zval {
let mut new = Zval::new();
new.u1 = self.u1;
new.value = self.value;
// SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
// from either of the variants.
//
// SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
// then it is valid to dereference `self.value.counted`.
unsafe {
let flags = ZvalTypeFlags::from_bits_unchecked(self.u1.type_info);
if flags.contains(ZvalTypeFlags::RefCounted) {
(*self.value.counted).gc.refcount += 1;
}
}
new
}
}
impl Debug for Zval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
}
impl Drop for Zval {
fn drop(&mut self) {
self.change_type(ZvalTypeFlags::Null);
}
}
impl Default for Zval {
fn default() -> Self {
Self::new()
}
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
pub fn get_property<'a, T>(&'a self, name: &str) -> Result<T>
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<bool> {
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)
}
}
/// Extracts some type from a Zend object.
///
/// This is a wrapper function around `FromZendObject::extract()`.
pub fn extract<'a, T>(&'a self) -> Result<T>
where
T: FromZendObject<'a>,
{
T::from_zend_object(self)
}
/// Returns an unique identifier for the object.
///
/// The id is guaranteed to be unique for the lifetime of the object.
/// Once the object is destroyed, it may be reused for other objects.
/// This is equivalent to calling the [`spl_object_id`] PHP function.
///
/// [`spl_object_id`]: https://www.php.net/manual/function.spl-object-id
#[inline]
pub fn get_id(&self) -> u32 {
self.handle
}
/// Computes an unique hash for the object.
///
/// The hash is guaranteed to be unique for the lifetime of the object.
/// Once the object is destroyed, it may be reused for other objects.
/// This is equivalent to calling the [`spl_object_hash`] PHP function.
///
/// [`spl_object_hash`]: https://www.php.net/manual/function.spl-object-hash.php
pub fn hash(&self) -> String {
format!("{:016x}0000000000000000", self.handle)
}
/// 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
}
}
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<Self> {
zval.object()
}
}
impl<'a> FromZvalMut<'a> for &'a mut ZendObject {
const TYPE: DataType = DataType::Object(None);
fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
zval.object_mut()
}
}
impl IntoZval for ZBox<ZendObject> {
const TYPE: DataType = DataType::Object(None);
#[inline]
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<'a> IntoZval for &'a mut ZendObject {
const TYPE: DataType = DataType::Object(None);
#[inline]
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
zv.set_object(self);
Ok(())
}
}
impl FromZendObject<'_> for String {
fn from_zend_object(obj: &ZendObject) -> Result<Self> {
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"),
);
}
}
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
unsafe fn internal<T: RegisteredClass>(
object: *mut ZendObject,
props: &mut ZendHashTable,
) -> PhpResult {
let obj = object
.as_mut()
.and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
.ok_or("Invalid object pointer given")?;
let self_ = &mut **obj;
let struct_props = T::get_metadata().get_properties();
for (name, val) in struct_props {
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(ZendHashTable::new().into_raw()))
.expect("Failed to get property hashtable");
if let Err(e) = internal::<T>(object, props) {
let _ = e.throw();
}
props
}
unsafe extern "C" fn has_property<T: RegisteredClass>(
object: *mut ZendObject,
member: *mut ZendStr,
has_set_exists: c_int,
cache_slot: *mut *mut c_void,
) -> c_int {
#[inline(always)]
unsafe fn internal<T: RegisteredClass>(
object: *mut ZendObject,
member: *mut ZendStr,
has_set_exists: c_int,
cache_slot: *mut *mut c_void,
) -> PhpResult<c_int> {
let obj = object
.as_mut()
.and_then(|obj| ZendClassObject::<T>::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_metadata().get_properties();
let prop = props.get(prop_name.as_str()?);
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,
))
}
sourcepub fn long(&self) -> Option<ZendLong>
pub fn long(&self) -> Option<ZendLong>
Returns the value of the zval if it is a long.
Examples found in repository?
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
pub fn double(&self) -> Option<f64> {
if self.is_double() {
Some(unsafe { self.value.dval })
} else {
self.long().map(|x| x as f64)
}
}
/// 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`].
pub fn zend_str(&self) -> Option<&ZendStr> {
if self.is_string() {
unsafe { self.value.str_.as_ref() }
} else {
None
}
}
/// 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()`]
/// function.
///
/// [`str()`]: #method.str
pub fn string(&self) -> Option<String> {
self.str()
.map(|s| s.to_string())
.or_else(|| self.double().map(|x| x.to_string()))
}
/// 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.
pub fn str(&self) -> Option<&str> {
self.zend_str().and_then(|zs| zs.as_str().ok())
}
/// 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`] function 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a string and can be unpacked into
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
/// except you can only unpack one type.
///
/// This function is similar to [`Zval::binary`] except that a slice is
/// returned instead of a vector, meaning the contents of the string is
/// not copied.
///
/// # 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a resource.
pub fn resource(&self) -> Option<*mut zend_resource> {
// TODO: Can we improve this function? I haven't done much research into
// resources so I don't know if this is the optimal way to return this.
if self.is_resource() {
Some(unsafe { self.value.res })
} else {
None
}
}
/// Returns an immutable reference to the underlying zval hashtable if the
/// zval contains an array.
pub fn array(&self) -> Option<&ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_ref() }
} else {
None
}
}
/// Returns a mutable reference to the underlying zval hashtable if the zval
/// contains an array.
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_mut() }
} else {
None
}
}
/// Returns the value of the zval if it is an object.
pub fn object(&self) -> Option<&ZendObject> {
if self.is_object() {
unsafe { self.value.obj.as_ref() }
} else {
None
}
}
/// 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() }
} else {
None
}
}
/// Returns the value of the zval if it is a reference.
pub fn reference(&self) -> Option<&Zval> {
if self.is_reference() {
Some(&unsafe { self.value.ref_.as_ref() }?.val)
} else {
None
}
}
/// Returns a mutable reference to the underlying zval if it is a reference.
pub fn reference_mut(&mut self) -> Option<&mut Zval> {
if self.is_reference() {
Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
} else {
None
}
}
/// Returns the value of the zval if it is callable.
pub fn callable(&self) -> Option<ZendCallable> {
// The Zval is checked if it is callable in the `new` function.
ZendCallable::new(self).ok()
}
/// Returns the value of the zval if it is a pointer.
///
/// # 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.
pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
if self.is_ptr() {
Some(self.value.ptr as *mut T)
} else {
None
}
}
/// 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.
///
/// # Parameters
///
/// * `params` - A list of parameters to call the function with.
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
self.callable().ok_or(Error::Callable)?.try_call(params)
}
/// Returns the type of the Zval.
pub fn get_type(&self) -> DataType {
DataType::from(unsafe { self.u1.v.type_ } as u32)
}
/// Returns true if the zval is a long, false otherwise.
pub fn is_long(&self) -> bool {
self.get_type() == DataType::Long
}
/// Returns true if the zval is null, false otherwise.
pub fn is_null(&self) -> bool {
self.get_type() == DataType::Null
}
/// Returns true if the zval is true, false otherwise.
pub fn is_true(&self) -> bool {
self.get_type() == DataType::True
}
/// Returns true if the zval is false, false otherwise.
pub fn is_false(&self) -> bool {
self.get_type() == DataType::False
}
/// Returns true if the zval is a bool, false otherwise.
pub fn is_bool(&self) -> bool {
self.is_true() || self.is_false()
}
/// Returns true if the zval is a double, false otherwise.
pub fn is_double(&self) -> bool {
self.get_type() == DataType::Double
}
/// Returns true if the zval is a string, false otherwise.
pub fn is_string(&self) -> bool {
self.get_type() == DataType::String
}
/// Returns true if the zval is a resource, false otherwise.
pub fn is_resource(&self) -> bool {
self.get_type() == DataType::Resource
}
/// Returns true if the zval is an array, false otherwise.
pub fn is_array(&self) -> bool {
self.get_type() == DataType::Array
}
/// Returns true if the zval is an object, false otherwise.
pub fn is_object(&self) -> bool {
matches!(self.get_type(), DataType::Object(_))
}
/// Returns true if the zval is a reference, false otherwise.
pub fn is_reference(&self) -> bool {
self.get_type() == DataType::Reference
}
/// Returns true if the zval is callable, false otherwise.
pub fn is_callable(&self) -> bool {
let ptr: *const Self = self;
unsafe { zend_is_callable(ptr as *mut Self, 0, std::ptr::null_mut()) }
}
/// Returns true if the zval contains a pointer, false otherwise.
pub fn is_ptr(&self) -> bool {
self.get_type() == DataType::Ptr
}
/// Sets the value of the zval as a string. Returns nothing in a result when
/// successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new(val, persistent));
Ok(())
}
/// Sets the value of the zval as a Zend string.
///
/// # Parameters
///
/// * `val` - String content.
pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
self.change_type(ZvalTypeFlags::StringEx);
self.value.str_ = val.into_raw();
}
/// Sets the value of the zval as a binary string, which is represented in
/// Rust as a vector.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
self.change_type(ZvalTypeFlags::StringEx);
let ptr = T::pack_into(val);
self.value.str_ = ptr;
}
/// Sets the value of the zval as a interned string. Returns nothing in a
/// result when successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new_interned(val, persistent));
Ok(())
}
/// Sets the value of the zval as a long.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
self._set_long(val.into())
}
fn _set_long(&mut self, val: ZendLong) {
self.change_type(ZvalTypeFlags::Long);
self.value.lval = val;
}
/// Sets the value of the zval as a double.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_double<T: Into<f64>>(&mut self, val: T) {
self._set_double(val.into())
}
fn _set_double(&mut self, val: f64) {
self.change_type(ZvalTypeFlags::Double);
self.value.dval = val;
}
/// Sets the value of the zval as a boolean.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_bool<T: Into<bool>>(&mut self, val: T) {
self._set_bool(val.into())
}
fn _set_bool(&mut self, val: bool) {
self.change_type(if val {
ZvalTypeFlags::True
} else {
ZvalTypeFlags::False
});
}
/// Sets the value of the zval as null.
///
/// This is the default of a zval.
pub fn set_null(&mut self) {
self.change_type(ZvalTypeFlags::Null);
}
/// Sets the value of the zval as a resource.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_resource(&mut self, val: *mut zend_resource) {
self.change_type(ZvalTypeFlags::ResourceEx);
self.value.res = val;
}
/// Sets the value of the zval as a reference to an object.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_object(&mut self, val: &mut ZendObject) {
self.change_type(ZvalTypeFlags::ObjectEx);
val.inc_count(); // TODO(david): not sure if this is needed :/
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.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
&mut self,
val: T,
) -> Result<()> {
self.set_hashtable(val.try_into()?);
Ok(())
}
/// Sets the value of the zval as an array. Returns nothing in a result on
/// success.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>) {
self.change_type(ZvalTypeFlags::ArrayEx);
self.value.arr = val.into_raw();
}
/// Sets the value of the zval as a pointer.
///
/// # Parameters
///
/// * `ptr` - The pointer to set the zval as.
pub fn set_ptr<T>(&mut self, ptr: *mut T) {
self.u1.type_info = ZvalTypeFlags::Ptr.bits();
self.value.ptr = ptr as *mut c_void;
}
/// 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.
pub(crate) fn release(mut self) {
// 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.
///
/// # 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.
unsafe { zval_ptr_dtor(self) };
self.u1.type_info = ty.bits();
}
/// Extracts some type from a `Zval`.
///
/// This is a wrapper function around `TryFrom`.
pub fn extract<'a, T>(&'a self) -> Option<T>
where
T: FromZval<'a>,
{
FromZval::from_zval(self)
}
/// Creates a shallow clone of the [`Zval`].
///
/// This copies the contents of the [`Zval`], and increments the reference
/// counter of the underlying value (if it is reference counted).
///
/// For example, if the zval contains a long, it will simply copy the value.
/// However, if the zval contains an object, the new zval will point to the
/// same object, and the objects reference counter will be incremented.
///
/// # Returns
///
/// The cloned zval.
pub fn shallow_clone(&self) -> Zval {
let mut new = Zval::new();
new.u1 = self.u1;
new.value = self.value;
// SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
// from either of the variants.
//
// SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
// then it is valid to dereference `self.value.counted`.
unsafe {
let flags = ZvalTypeFlags::from_bits_unchecked(self.u1.type_info);
if flags.contains(ZvalTypeFlags::RefCounted) {
(*self.value.counted).gc.refcount += 1;
}
}
new
}
}
impl Debug for Zval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn bool(&self) -> Option<bool>
pub fn bool(&self) -> Option<bool>
Returns the value of the zval if it is a bool.
Examples found in repository?
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn double(&self) -> Option<f64>
pub fn double(&self) -> Option<f64>
Returns the value of the zval if it is a double.
Examples found in repository?
More examples
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
pub fn string(&self) -> Option<String> {
self.str()
.map(|s| s.to_string())
.or_else(|| self.double().map(|x| x.to_string()))
}
/// 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.
pub fn str(&self) -> Option<&str> {
self.zend_str().and_then(|zs| zs.as_str().ok())
}
/// 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`] function 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a string and can be unpacked into
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
/// except you can only unpack one type.
///
/// This function is similar to [`Zval::binary`] except that a slice is
/// returned instead of a vector, meaning the contents of the string is
/// not copied.
///
/// # 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a resource.
pub fn resource(&self) -> Option<*mut zend_resource> {
// TODO: Can we improve this function? I haven't done much research into
// resources so I don't know if this is the optimal way to return this.
if self.is_resource() {
Some(unsafe { self.value.res })
} else {
None
}
}
/// Returns an immutable reference to the underlying zval hashtable if the
/// zval contains an array.
pub fn array(&self) -> Option<&ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_ref() }
} else {
None
}
}
/// Returns a mutable reference to the underlying zval hashtable if the zval
/// contains an array.
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_mut() }
} else {
None
}
}
/// Returns the value of the zval if it is an object.
pub fn object(&self) -> Option<&ZendObject> {
if self.is_object() {
unsafe { self.value.obj.as_ref() }
} else {
None
}
}
/// 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() }
} else {
None
}
}
/// Returns the value of the zval if it is a reference.
pub fn reference(&self) -> Option<&Zval> {
if self.is_reference() {
Some(&unsafe { self.value.ref_.as_ref() }?.val)
} else {
None
}
}
/// Returns a mutable reference to the underlying zval if it is a reference.
pub fn reference_mut(&mut self) -> Option<&mut Zval> {
if self.is_reference() {
Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
} else {
None
}
}
/// Returns the value of the zval if it is callable.
pub fn callable(&self) -> Option<ZendCallable> {
// The Zval is checked if it is callable in the `new` function.
ZendCallable::new(self).ok()
}
/// Returns the value of the zval if it is a pointer.
///
/// # 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.
pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
if self.is_ptr() {
Some(self.value.ptr as *mut T)
} else {
None
}
}
/// 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.
///
/// # Parameters
///
/// * `params` - A list of parameters to call the function with.
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
self.callable().ok_or(Error::Callable)?.try_call(params)
}
/// Returns the type of the Zval.
pub fn get_type(&self) -> DataType {
DataType::from(unsafe { self.u1.v.type_ } as u32)
}
/// Returns true if the zval is a long, false otherwise.
pub fn is_long(&self) -> bool {
self.get_type() == DataType::Long
}
/// Returns true if the zval is null, false otherwise.
pub fn is_null(&self) -> bool {
self.get_type() == DataType::Null
}
/// Returns true if the zval is true, false otherwise.
pub fn is_true(&self) -> bool {
self.get_type() == DataType::True
}
/// Returns true if the zval is false, false otherwise.
pub fn is_false(&self) -> bool {
self.get_type() == DataType::False
}
/// Returns true if the zval is a bool, false otherwise.
pub fn is_bool(&self) -> bool {
self.is_true() || self.is_false()
}
/// Returns true if the zval is a double, false otherwise.
pub fn is_double(&self) -> bool {
self.get_type() == DataType::Double
}
/// Returns true if the zval is a string, false otherwise.
pub fn is_string(&self) -> bool {
self.get_type() == DataType::String
}
/// Returns true if the zval is a resource, false otherwise.
pub fn is_resource(&self) -> bool {
self.get_type() == DataType::Resource
}
/// Returns true if the zval is an array, false otherwise.
pub fn is_array(&self) -> bool {
self.get_type() == DataType::Array
}
/// Returns true if the zval is an object, false otherwise.
pub fn is_object(&self) -> bool {
matches!(self.get_type(), DataType::Object(_))
}
/// Returns true if the zval is a reference, false otherwise.
pub fn is_reference(&self) -> bool {
self.get_type() == DataType::Reference
}
/// Returns true if the zval is callable, false otherwise.
pub fn is_callable(&self) -> bool {
let ptr: *const Self = self;
unsafe { zend_is_callable(ptr as *mut Self, 0, std::ptr::null_mut()) }
}
/// Returns true if the zval contains a pointer, false otherwise.
pub fn is_ptr(&self) -> bool {
self.get_type() == DataType::Ptr
}
/// Sets the value of the zval as a string. Returns nothing in a result when
/// successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new(val, persistent));
Ok(())
}
/// Sets the value of the zval as a Zend string.
///
/// # Parameters
///
/// * `val` - String content.
pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
self.change_type(ZvalTypeFlags::StringEx);
self.value.str_ = val.into_raw();
}
/// Sets the value of the zval as a binary string, which is represented in
/// Rust as a vector.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
self.change_type(ZvalTypeFlags::StringEx);
let ptr = T::pack_into(val);
self.value.str_ = ptr;
}
/// Sets the value of the zval as a interned string. Returns nothing in a
/// result when successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new_interned(val, persistent));
Ok(())
}
/// Sets the value of the zval as a long.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
self._set_long(val.into())
}
fn _set_long(&mut self, val: ZendLong) {
self.change_type(ZvalTypeFlags::Long);
self.value.lval = val;
}
/// Sets the value of the zval as a double.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_double<T: Into<f64>>(&mut self, val: T) {
self._set_double(val.into())
}
fn _set_double(&mut self, val: f64) {
self.change_type(ZvalTypeFlags::Double);
self.value.dval = val;
}
/// Sets the value of the zval as a boolean.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_bool<T: Into<bool>>(&mut self, val: T) {
self._set_bool(val.into())
}
fn _set_bool(&mut self, val: bool) {
self.change_type(if val {
ZvalTypeFlags::True
} else {
ZvalTypeFlags::False
});
}
/// Sets the value of the zval as null.
///
/// This is the default of a zval.
pub fn set_null(&mut self) {
self.change_type(ZvalTypeFlags::Null);
}
/// Sets the value of the zval as a resource.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_resource(&mut self, val: *mut zend_resource) {
self.change_type(ZvalTypeFlags::ResourceEx);
self.value.res = val;
}
/// Sets the value of the zval as a reference to an object.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_object(&mut self, val: &mut ZendObject) {
self.change_type(ZvalTypeFlags::ObjectEx);
val.inc_count(); // TODO(david): not sure if this is needed :/
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.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
&mut self,
val: T,
) -> Result<()> {
self.set_hashtable(val.try_into()?);
Ok(())
}
/// Sets the value of the zval as an array. Returns nothing in a result on
/// success.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>) {
self.change_type(ZvalTypeFlags::ArrayEx);
self.value.arr = val.into_raw();
}
/// Sets the value of the zval as a pointer.
///
/// # Parameters
///
/// * `ptr` - The pointer to set the zval as.
pub fn set_ptr<T>(&mut self, ptr: *mut T) {
self.u1.type_info = ZvalTypeFlags::Ptr.bits();
self.value.ptr = ptr as *mut c_void;
}
/// 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.
pub(crate) fn release(mut self) {
// 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.
///
/// # 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.
unsafe { zval_ptr_dtor(self) };
self.u1.type_info = ty.bits();
}
/// Extracts some type from a `Zval`.
///
/// This is a wrapper function around `TryFrom`.
pub fn extract<'a, T>(&'a self) -> Option<T>
where
T: FromZval<'a>,
{
FromZval::from_zval(self)
}
/// Creates a shallow clone of the [`Zval`].
///
/// This copies the contents of the [`Zval`], and increments the reference
/// counter of the underlying value (if it is reference counted).
///
/// For example, if the zval contains a long, it will simply copy the value.
/// However, if the zval contains an object, the new zval will point to the
/// same object, and the objects reference counter will be incremented.
///
/// # Returns
///
/// The cloned zval.
pub fn shallow_clone(&self) -> Zval {
let mut new = Zval::new();
new.u1 = self.u1;
new.value = self.value;
// SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
// from either of the variants.
//
// SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
// then it is valid to dereference `self.value.counted`.
unsafe {
let flags = ZvalTypeFlags::from_bits_unchecked(self.u1.type_info);
if flags.contains(ZvalTypeFlags::RefCounted) {
(*self.value.counted).gc.refcount += 1;
}
}
new
}
}
impl Debug for Zval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn zend_str(&self) -> Option<&ZendStr>
pub fn zend_str(&self) -> Option<&ZendStr>
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()
, as this function does not attempt to
convert other types into a String
.
Examples found in repository?
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
pub fn str(&self) -> Option<&str> {
self.zend_str().and_then(|zs| zs.as_str().ok())
}
/// 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`] function 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a string and can be unpacked into
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
/// except you can only unpack one type.
///
/// This function is similar to [`Zval::binary`] except that a slice is
/// returned instead of a vector, meaning the contents of the string is
/// not copied.
///
/// # 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
self.zend_str().map(T::unpack_into)
}
sourcepub fn string(&self) -> Option<String>
pub fn string(&self) -> Option<String>
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()
function.
Examples found in repository?
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn str(&self) -> Option<&str>
pub fn str(&self) -> Option<&str>
Returns the value of the zval if it is a string.
Note that this functions output will not be the same as
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.
Examples found in repository?
More examples
sourcepub fn binary<T: Pack>(&self) -> Option<Vec<T>>
pub fn binary<T: Pack>(&self) -> Option<Vec<T>>
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
function 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
documentation for more details.
sourcepub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]>
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]>
Returns the value of the zval if it is a string and can be unpacked into
a slice of a given type. Similar to the unpack
function in PHP,
except you can only unpack one type.
This function is similar to Zval::binary
except that a slice is
returned instead of a vector, meaning the contents of the string is
not copied.
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
documentation for more details.
sourcepub fn resource(&self) -> Option<*mut zend_resource>
pub fn resource(&self) -> Option<*mut zend_resource>
Returns the value of the zval if it is a resource.
Examples found in repository?
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn array(&self) -> Option<&ZendHashTable>
pub fn array(&self) -> Option<&ZendHashTable>
Returns an immutable reference to the underlying zval hashtable if the zval contains an array.
Examples found in repository?
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array()
}
}
///////////////////////////////////////////
//// HashMap
///////////////////////////////////////////
impl<'a, V> TryFrom<&'a ZendHashTable> for HashMap<String, V>
where
V: FromZval<'a>,
{
type Error = Error;
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut hm = HashMap::with_capacity(value.len());
for (idx, key, val) in value.iter() {
hm.insert(
key.unwrap_or_else(|| idx.to_string()),
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
);
}
Ok(hm)
}
}
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<ZendHashTable>
where
K: AsRef<str>,
V: IntoZval,
{
type Error = Error;
fn try_from(value: HashMap<K, V>) -> Result<Self> {
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)?;
}
Ok(ht)
}
}
impl<K, V> IntoZval for HashMap<K, V>
where
K: AsRef<str>,
V: IntoZval,
{
const TYPE: DataType = DataType::Array;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let arr = self.try_into()?;
zv.set_hashtable(arr);
Ok(())
}
}
impl<'a, T> FromZval<'a> for HashMap<String, T>
where
T: FromZval<'a>,
{
const TYPE: DataType = DataType::Array;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array().and_then(|arr| arr.try_into().ok())
}
}
///////////////////////////////////////////
//// Vec
///////////////////////////////////////////
impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
where
T: FromZval<'a>,
{
type Error = Error;
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut vec = Vec::with_capacity(value.len());
for (_, _, val) in value.iter() {
vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
}
Ok(vec)
}
}
impl<T> TryFrom<Vec<T>> for ZBox<ZendHashTable>
where
T: IntoZval,
{
type Error = Error;
fn try_from(value: Vec<T>) -> Result<Self> {
let mut ht = ZendHashTable::with_capacity(
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
);
for val in value.into_iter() {
ht.push(val)?;
}
Ok(ht)
}
}
impl<T> IntoZval for Vec<T>
where
T: IntoZval,
{
const TYPE: DataType = DataType::Array;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let arr = self.try_into()?;
zv.set_hashtable(arr);
Ok(())
}
}
impl<'a, T> FromZval<'a> for Vec<T>
where
T: FromZval<'a>,
{
const TYPE: DataType = DataType::Array;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array().and_then(|arr| arr.try_into().ok())
}
More examples
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn array_mut(&mut self) -> Option<&mut ZendHashTable>
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable>
Returns a mutable reference to the underlying zval hashtable if the zval contains an array.
sourcepub fn object(&self) -> Option<&ZendObject>
pub fn object(&self) -> Option<&ZendObject>
Returns the value of the zval if it is an object.
Examples found in repository?
More examples
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn object_mut(&mut self) -> Option<&mut ZendObject>
pub fn object_mut(&mut self) -> Option<&mut ZendObject>
Returns a mutable reference to the object contained in the Zval
, if
any.
Examples found in repository?
More examples
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
pub fn parser_object(&mut self) -> (ArgParser<'_, '_>, Option<&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.
let arg = unsafe { self.zend_call_arg(i as usize) };
args.push(arg);
}
let obj = self.This.object_mut();
(ArgParser::new(args), obj)
}
/// 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.
///
/// 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`.
///
/// # 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::<Example>();
/// 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<T: RegisteredClass>(
&mut self,
) -> (ArgParser<'_, '_>, Option<&mut ZendClassObject<T>>) {
let (parser, obj) = self.parser_object();
(
parser,
obj.and_then(|obj| ZendClassObject::from_zend_obj_mut(obj)),
)
}
/// 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`].
///
/// # 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::<Example>();
/// dbg!(this);
/// }
///
/// #[php_module]
/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
/// module
/// }
/// ```
pub fn get_object<T: RegisteredClass>(&mut self) -> Option<&mut ZendClassObject<T>> {
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.
self.This.object_mut()
}
sourcepub fn reference(&self) -> Option<&Zval>
pub fn reference(&self) -> Option<&Zval>
Returns the value of the zval if it is a reference.
Examples found in repository?
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn reference_mut(&mut self) -> Option<&mut Zval>
pub fn reference_mut(&mut self) -> Option<&mut Zval>
Returns a mutable reference to the underlying zval if it is a reference.
sourcepub fn callable(&self) -> Option<ZendCallable<'_>>
pub fn callable(&self) -> Option<ZendCallable<'_>>
Returns the value of the zval if it is callable.
sourcepub unsafe fn ptr<T>(&self) -> Option<*mut T>
pub unsafe fn ptr<T>(&self) -> Option<*mut T>
Returns the value of the zval if it is a pointer.
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.
Examples found in repository?
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
sourcepub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval>
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<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.
You should not call this function directly, rather through the
call_user_func
macro.
Parameters
params
- A list of parameters to call the function with.
sourcepub fn get_type(&self) -> DataType
pub fn get_type(&self) -> DataType
Returns the type of the Zval.
Examples found in repository?
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
pub fn is_long(&self) -> bool {
self.get_type() == DataType::Long
}
/// Returns true if the zval is null, false otherwise.
pub fn is_null(&self) -> bool {
self.get_type() == DataType::Null
}
/// Returns true if the zval is true, false otherwise.
pub fn is_true(&self) -> bool {
self.get_type() == DataType::True
}
/// Returns true if the zval is false, false otherwise.
pub fn is_false(&self) -> bool {
self.get_type() == DataType::False
}
/// Returns true if the zval is a bool, false otherwise.
pub fn is_bool(&self) -> bool {
self.is_true() || self.is_false()
}
/// Returns true if the zval is a double, false otherwise.
pub fn is_double(&self) -> bool {
self.get_type() == DataType::Double
}
/// Returns true if the zval is a string, false otherwise.
pub fn is_string(&self) -> bool {
self.get_type() == DataType::String
}
/// Returns true if the zval is a resource, false otherwise.
pub fn is_resource(&self) -> bool {
self.get_type() == DataType::Resource
}
/// Returns true if the zval is an array, false otherwise.
pub fn is_array(&self) -> bool {
self.get_type() == DataType::Array
}
/// Returns true if the zval is an object, false otherwise.
pub fn is_object(&self) -> bool {
matches!(self.get_type(), DataType::Object(_))
}
/// Returns true if the zval is a reference, false otherwise.
pub fn is_reference(&self) -> bool {
self.get_type() == DataType::Reference
}
/// Returns true if the zval is callable, false otherwise.
pub fn is_callable(&self) -> bool {
let ptr: *const Self = self;
unsafe { zend_is_callable(ptr as *mut Self, 0, std::ptr::null_mut()) }
}
/// Returns true if the zval contains a pointer, false otherwise.
pub fn is_ptr(&self) -> bool {
self.get_type() == DataType::Ptr
}
/// Sets the value of the zval as a string. Returns nothing in a result when
/// successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new(val, persistent));
Ok(())
}
/// Sets the value of the zval as a Zend string.
///
/// # Parameters
///
/// * `val` - String content.
pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
self.change_type(ZvalTypeFlags::StringEx);
self.value.str_ = val.into_raw();
}
/// Sets the value of the zval as a binary string, which is represented in
/// Rust as a vector.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
self.change_type(ZvalTypeFlags::StringEx);
let ptr = T::pack_into(val);
self.value.str_ = ptr;
}
/// Sets the value of the zval as a interned string. Returns nothing in a
/// result when successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new_interned(val, persistent));
Ok(())
}
/// Sets the value of the zval as a long.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
self._set_long(val.into())
}
fn _set_long(&mut self, val: ZendLong) {
self.change_type(ZvalTypeFlags::Long);
self.value.lval = val;
}
/// Sets the value of the zval as a double.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_double<T: Into<f64>>(&mut self, val: T) {
self._set_double(val.into())
}
fn _set_double(&mut self, val: f64) {
self.change_type(ZvalTypeFlags::Double);
self.value.dval = val;
}
/// Sets the value of the zval as a boolean.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_bool<T: Into<bool>>(&mut self, val: T) {
self._set_bool(val.into())
}
fn _set_bool(&mut self, val: bool) {
self.change_type(if val {
ZvalTypeFlags::True
} else {
ZvalTypeFlags::False
});
}
/// Sets the value of the zval as null.
///
/// This is the default of a zval.
pub fn set_null(&mut self) {
self.change_type(ZvalTypeFlags::Null);
}
/// Sets the value of the zval as a resource.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_resource(&mut self, val: *mut zend_resource) {
self.change_type(ZvalTypeFlags::ResourceEx);
self.value.res = val;
}
/// Sets the value of the zval as a reference to an object.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_object(&mut self, val: &mut ZendObject) {
self.change_type(ZvalTypeFlags::ObjectEx);
val.inc_count(); // TODO(david): not sure if this is needed :/
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.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
&mut self,
val: T,
) -> Result<()> {
self.set_hashtable(val.try_into()?);
Ok(())
}
/// Sets the value of the zval as an array. Returns nothing in a result on
/// success.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>) {
self.change_type(ZvalTypeFlags::ArrayEx);
self.value.arr = val.into_raw();
}
/// Sets the value of the zval as a pointer.
///
/// # Parameters
///
/// * `ptr` - The pointer to set the zval as.
pub fn set_ptr<T>(&mut self, ptr: *mut T) {
self.u1.type_info = ZvalTypeFlags::Ptr.bits();
self.value.ptr = ptr as *mut c_void;
}
/// 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.
pub(crate) fn release(mut self) {
// 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.
///
/// # 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.
unsafe { zval_ptr_dtor(self) };
self.u1.type_info = ty.bits();
}
/// Extracts some type from a `Zval`.
///
/// This is a wrapper function around `TryFrom`.
pub fn extract<'a, T>(&'a self) -> Option<T>
where
T: FromZval<'a>,
{
FromZval::from_zval(self)
}
/// Creates a shallow clone of the [`Zval`].
///
/// This copies the contents of the [`Zval`], and increments the reference
/// counter of the underlying value (if it is reference counted).
///
/// For example, if the zval contains a long, it will simply copy the value.
/// However, if the zval contains an object, the new zval will point to the
/// same object, and the objects reference counter will be incremented.
///
/// # Returns
///
/// The cloned zval.
pub fn shallow_clone(&self) -> Zval {
let mut new = Zval::new();
new.u1 = self.u1;
new.value = self.value;
// SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
// from either of the variants.
//
// SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
// then it is valid to dereference `self.value.counted`.
unsafe {
let flags = ZvalTypeFlags::from_bits_unchecked(self.u1.type_info);
if flags.contains(ZvalTypeFlags::RefCounted) {
(*self.value.counted).gc.refcount += 1;
}
}
new
}
}
impl Debug for Zval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("Zval");
let ty = self.get_type();
dbg.field("type", &ty);
macro_rules! field {
($value: expr) => {
dbg.field("val", &$value)
};
}
match ty {
DataType::Undef => field!(Option::<()>::None),
DataType::Null => field!(Option::<()>::None),
DataType::False => field!(false),
DataType::True => field!(true),
DataType::Long => field!(self.long()),
DataType::Double => field!(self.double()),
DataType::String | DataType::Mixed => field!(self.string()),
DataType::Array => field!(self.array()),
DataType::Object(_) => field!(self.object()),
DataType::Resource => field!(self.resource()),
DataType::Reference => field!(self.reference()),
DataType::Callable => field!(self.string()),
DataType::ConstantExpression => field!(Option::<()>::None),
DataType::Void => field!(Option::<()>::None),
DataType::Bool => field!(self.bool()),
// SAFETY: We are not accessing the pointer.
DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
};
dbg.finish()
}
More examples
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut hm = HashMap::with_capacity(value.len());
for (idx, key, val) in value.iter() {
hm.insert(
key.unwrap_or_else(|| idx.to_string()),
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
);
}
Ok(hm)
}
}
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<ZendHashTable>
where
K: AsRef<str>,
V: IntoZval,
{
type Error = Error;
fn try_from(value: HashMap<K, V>) -> Result<Self> {
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)?;
}
Ok(ht)
}
}
impl<K, V> IntoZval for HashMap<K, V>
where
K: AsRef<str>,
V: IntoZval,
{
const TYPE: DataType = DataType::Array;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let arr = self.try_into()?;
zv.set_hashtable(arr);
Ok(())
}
}
impl<'a, T> FromZval<'a> for HashMap<String, T>
where
T: FromZval<'a>,
{
const TYPE: DataType = DataType::Array;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array().and_then(|arr| arr.try_into().ok())
}
}
///////////////////////////////////////////
//// Vec
///////////////////////////////////////////
impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
where
T: FromZval<'a>,
{
type Error = Error;
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut vec = Vec::with_capacity(value.len());
for (_, _, val) in value.iter() {
vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
}
Ok(vec)
}
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
pub fn get_property<'a, T>(&'a self, name: &str) -> Result<T>
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()))
}
sourcepub fn is_null(&self) -> bool
pub fn is_null(&self) -> bool
Returns true if the zval is null, false otherwise.
Examples found in repository?
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
unsafe fn internal<T: RegisteredClass>(
object: *mut ZendObject,
member: *mut ZendStr,
has_set_exists: c_int,
cache_slot: *mut *mut c_void,
) -> PhpResult<c_int> {
let obj = object
.as_mut()
.and_then(|obj| ZendClassObject::<T>::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_metadata().get_properties();
let prop = props.get(prop_name.as_str()?);
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,
))
}
sourcepub fn is_true(&self) -> bool
pub fn is_true(&self) -> bool
Returns true if the zval is true, false otherwise.
Examples found in repository?
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
pub fn bool(&self) -> Option<bool> {
if self.is_true() {
Some(true)
} else if self.is_false() {
Some(false)
} else {
None
}
}
/// Returns the value of the zval if it is a double.
pub fn double(&self) -> Option<f64> {
if self.is_double() {
Some(unsafe { self.value.dval })
} else {
self.long().map(|x| x as f64)
}
}
/// 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`].
pub fn zend_str(&self) -> Option<&ZendStr> {
if self.is_string() {
unsafe { self.value.str_.as_ref() }
} else {
None
}
}
/// 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()`]
/// function.
///
/// [`str()`]: #method.str
pub fn string(&self) -> Option<String> {
self.str()
.map(|s| s.to_string())
.or_else(|| self.double().map(|x| x.to_string()))
}
/// 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.
pub fn str(&self) -> Option<&str> {
self.zend_str().and_then(|zs| zs.as_str().ok())
}
/// 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`] function 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a string and can be unpacked into
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
/// except you can only unpack one type.
///
/// This function is similar to [`Zval::binary`] except that a slice is
/// returned instead of a vector, meaning the contents of the string is
/// not copied.
///
/// # 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a resource.
pub fn resource(&self) -> Option<*mut zend_resource> {
// TODO: Can we improve this function? I haven't done much research into
// resources so I don't know if this is the optimal way to return this.
if self.is_resource() {
Some(unsafe { self.value.res })
} else {
None
}
}
/// Returns an immutable reference to the underlying zval hashtable if the
/// zval contains an array.
pub fn array(&self) -> Option<&ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_ref() }
} else {
None
}
}
/// Returns a mutable reference to the underlying zval hashtable if the zval
/// contains an array.
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_mut() }
} else {
None
}
}
/// Returns the value of the zval if it is an object.
pub fn object(&self) -> Option<&ZendObject> {
if self.is_object() {
unsafe { self.value.obj.as_ref() }
} else {
None
}
}
/// 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() }
} else {
None
}
}
/// Returns the value of the zval if it is a reference.
pub fn reference(&self) -> Option<&Zval> {
if self.is_reference() {
Some(&unsafe { self.value.ref_.as_ref() }?.val)
} else {
None
}
}
/// Returns a mutable reference to the underlying zval if it is a reference.
pub fn reference_mut(&mut self) -> Option<&mut Zval> {
if self.is_reference() {
Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
} else {
None
}
}
/// Returns the value of the zval if it is callable.
pub fn callable(&self) -> Option<ZendCallable> {
// The Zval is checked if it is callable in the `new` function.
ZendCallable::new(self).ok()
}
/// Returns the value of the zval if it is a pointer.
///
/// # 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.
pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
if self.is_ptr() {
Some(self.value.ptr as *mut T)
} else {
None
}
}
/// 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.
///
/// # Parameters
///
/// * `params` - A list of parameters to call the function with.
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
self.callable().ok_or(Error::Callable)?.try_call(params)
}
/// Returns the type of the Zval.
pub fn get_type(&self) -> DataType {
DataType::from(unsafe { self.u1.v.type_ } as u32)
}
/// Returns true if the zval is a long, false otherwise.
pub fn is_long(&self) -> bool {
self.get_type() == DataType::Long
}
/// Returns true if the zval is null, false otherwise.
pub fn is_null(&self) -> bool {
self.get_type() == DataType::Null
}
/// Returns true if the zval is true, false otherwise.
pub fn is_true(&self) -> bool {
self.get_type() == DataType::True
}
/// Returns true if the zval is false, false otherwise.
pub fn is_false(&self) -> bool {
self.get_type() == DataType::False
}
/// Returns true if the zval is a bool, false otherwise.
pub fn is_bool(&self) -> bool {
self.is_true() || self.is_false()
}
sourcepub fn is_false(&self) -> bool
pub fn is_false(&self) -> bool
Returns true if the zval is false, false otherwise.
Examples found in repository?
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
pub fn bool(&self) -> Option<bool> {
if self.is_true() {
Some(true)
} else if self.is_false() {
Some(false)
} else {
None
}
}
/// Returns the value of the zval if it is a double.
pub fn double(&self) -> Option<f64> {
if self.is_double() {
Some(unsafe { self.value.dval })
} else {
self.long().map(|x| x as f64)
}
}
/// 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`].
pub fn zend_str(&self) -> Option<&ZendStr> {
if self.is_string() {
unsafe { self.value.str_.as_ref() }
} else {
None
}
}
/// 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()`]
/// function.
///
/// [`str()`]: #method.str
pub fn string(&self) -> Option<String> {
self.str()
.map(|s| s.to_string())
.or_else(|| self.double().map(|x| x.to_string()))
}
/// 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.
pub fn str(&self) -> Option<&str> {
self.zend_str().and_then(|zs| zs.as_str().ok())
}
/// 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`] function 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a string and can be unpacked into
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
/// except you can only unpack one type.
///
/// This function is similar to [`Zval::binary`] except that a slice is
/// returned instead of a vector, meaning the contents of the string is
/// not copied.
///
/// # 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
/// documentation for more details.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
self.zend_str().map(T::unpack_into)
}
/// Returns the value of the zval if it is a resource.
pub fn resource(&self) -> Option<*mut zend_resource> {
// TODO: Can we improve this function? I haven't done much research into
// resources so I don't know if this is the optimal way to return this.
if self.is_resource() {
Some(unsafe { self.value.res })
} else {
None
}
}
/// Returns an immutable reference to the underlying zval hashtable if the
/// zval contains an array.
pub fn array(&self) -> Option<&ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_ref() }
} else {
None
}
}
/// Returns a mutable reference to the underlying zval hashtable if the zval
/// contains an array.
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_mut() }
} else {
None
}
}
/// Returns the value of the zval if it is an object.
pub fn object(&self) -> Option<&ZendObject> {
if self.is_object() {
unsafe { self.value.obj.as_ref() }
} else {
None
}
}
/// 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() }
} else {
None
}
}
/// Returns the value of the zval if it is a reference.
pub fn reference(&self) -> Option<&Zval> {
if self.is_reference() {
Some(&unsafe { self.value.ref_.as_ref() }?.val)
} else {
None
}
}
/// Returns a mutable reference to the underlying zval if it is a reference.
pub fn reference_mut(&mut self) -> Option<&mut Zval> {
if self.is_reference() {
Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
} else {
None
}
}
/// Returns the value of the zval if it is callable.
pub fn callable(&self) -> Option<ZendCallable> {
// The Zval is checked if it is callable in the `new` function.
ZendCallable::new(self).ok()
}
/// Returns the value of the zval if it is a pointer.
///
/// # 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.
pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
if self.is_ptr() {
Some(self.value.ptr as *mut T)
} else {
None
}
}
/// 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.
///
/// # Parameters
///
/// * `params` - A list of parameters to call the function with.
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
self.callable().ok_or(Error::Callable)?.try_call(params)
}
/// Returns the type of the Zval.
pub fn get_type(&self) -> DataType {
DataType::from(unsafe { self.u1.v.type_ } as u32)
}
/// Returns true if the zval is a long, false otherwise.
pub fn is_long(&self) -> bool {
self.get_type() == DataType::Long
}
/// Returns true if the zval is null, false otherwise.
pub fn is_null(&self) -> bool {
self.get_type() == DataType::Null
}
/// Returns true if the zval is true, false otherwise.
pub fn is_true(&self) -> bool {
self.get_type() == DataType::True
}
/// Returns true if the zval is false, false otherwise.
pub fn is_false(&self) -> bool {
self.get_type() == DataType::False
}
/// Returns true if the zval is a bool, false otherwise.
pub fn is_bool(&self) -> bool {
self.is_true() || self.is_false()
}
sourcepub fn is_resource(&self) -> bool
pub fn is_resource(&self) -> bool
Returns true if the zval is a resource, false otherwise.
sourcepub fn is_array(&self) -> bool
pub fn is_array(&self) -> bool
Returns true if the zval is an array, false otherwise.
Examples found in repository?
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
pub fn array(&self) -> Option<&ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_ref() }
} else {
None
}
}
/// Returns a mutable reference to the underlying zval hashtable if the zval
/// contains an array.
pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
if self.is_array() {
unsafe { self.value.arr.as_mut() }
} else {
None
}
}
sourcepub fn is_object(&self) -> bool
pub fn is_object(&self) -> bool
Returns true if the zval is an object, false otherwise.
Examples found in repository?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
pub fn object(&self) -> Option<&ZendObject> {
if self.is_object() {
unsafe { self.value.obj.as_ref() }
} else {
None
}
}
/// 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() }
} else {
None
}
}
sourcepub fn is_reference(&self) -> bool
pub fn is_reference(&self) -> bool
Returns true if the zval is a reference, false otherwise.
Examples found in repository?
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
pub fn reference(&self) -> Option<&Zval> {
if self.is_reference() {
Some(&unsafe { self.value.ref_.as_ref() }?.val)
} else {
None
}
}
/// Returns a mutable reference to the underlying zval if it is a reference.
pub fn reference_mut(&mut self) -> Option<&mut Zval> {
if self.is_reference() {
Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
} else {
None
}
}
sourcepub fn is_callable(&self) -> bool
pub fn is_callable(&self) -> bool
Returns true if the zval is callable, false otherwise.
Examples found in repository?
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
pub fn new(callable: &'a Zval) -> Result<Self> {
if callable.is_callable() {
Ok(Self(OwnedZval::Reference(callable)))
} else {
Err(Error::Callable)
}
}
/// Attempts to create a new [`ZendCallable`] by taking ownership of a Zval.
/// Returns a result containing the callable if the zval was callable.
///
/// # Parameters
///
/// * `callable` - The underlying [`Zval`] that is callable.
pub fn new_owned(callable: Zval) -> Result<Self> {
if callable.is_callable() {
Ok(Self(OwnedZval::Owned(callable)))
} else {
Err(Error::Callable)
}
}
/// Attempts to create a new [`ZendCallable`] from a function name. Returns
/// a result containing the callable if the function existed and was
/// callable.
///
/// # Parameters
///
/// * `name` - Name of the callable function.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::types::ZendCallable;
///
/// let strpos = ZendCallable::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<Self> {
let mut callable = Zval::new();
callable.set_string(name, false)?;
Self::new_owned(callable)
}
/// Attempts to call the callable with a list of arguments to pass to the
/// function.
///
/// You should not call this function directly, rather through the
/// [`call_user_func`] macro.
///
/// # 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::ZendCallable;
///
/// let strpos = ZendCallable::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<Zval> {
if !self.0.is_callable() {
return Err(Error::Callable);
}
let mut retval = Zval::new();
let len = params.len();
let params = params
.into_iter()
.map(|val| val.as_zval(false))
.collect::<Result<Vec<_>>>()?;
let packed = params.into_boxed_slice();
let result = unsafe {
_call_user_function_impl(
std::ptr::null_mut(),
self.0.as_ref() as *const crate::ffi::_zval_struct as *mut crate::ffi::_zval_struct,
&mut retval,
len as _,
packed.as_ptr() as *mut _,
std::ptr::null_mut(),
)
};
if result < 0 {
Err(Error::Callable)
} else if let Some(e) = ExecutorGlobals::take_exception() {
Err(Error::Exception(e))
} else {
Ok(retval)
}
}
sourcepub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()>
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()>
Sets the value of the zval as a string. Returns nothing in a result when successful.
Parameters
val
- The value to set the zval as.persistent
- Whether the string should persist between requests.
sourcepub fn set_zend_string(&mut self, val: ZBox<ZendStr>)
pub fn set_zend_string(&mut self, val: ZBox<ZendStr>)
Examples found in repository?
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new(val, persistent));
Ok(())
}
/// Sets the value of the zval as a Zend string.
///
/// # Parameters
///
/// * `val` - String content.
pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
self.change_type(ZvalTypeFlags::StringEx);
self.value.str_ = val.into_raw();
}
/// Sets the value of the zval as a binary string, which is represented in
/// Rust as a vector.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
self.change_type(ZvalTypeFlags::StringEx);
let ptr = T::pack_into(val);
self.value.str_ = ptr;
}
/// Sets the value of the zval as a interned string. Returns nothing in a
/// result when successful.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `persistent` - Whether the string should persist between requests.
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
self.set_zend_string(ZendStr::new_interned(val, persistent));
Ok(())
}
sourcepub fn set_binary<T: Pack>(&mut self, val: Vec<T>)
pub fn set_binary<T: Pack>(&mut self, val: Vec<T>)
Sets the value of the zval as a binary string, which is represented in Rust as a vector.
Parameters
val
- The value to set the zval as.
sourcepub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()>
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()>
Sets the value of the zval as a interned string. Returns nothing in a result when successful.
Parameters
val
- The value to set the zval as.persistent
- Whether the string should persist between requests.
sourcepub fn set_double<T: Into<f64>>(&mut self, val: T)
pub fn set_double<T: Into<f64>>(&mut self, val: T)
sourcepub fn set_null(&mut self)
pub fn set_null(&mut self)
Sets the value of the zval as null.
This is the default of a zval.
Examples found in repository?
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
zv.set_null();
Ok(())
}
}
impl<T> IntoZval for Option<T>
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(())
}
}
}
More examples
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
unsafe extern "C" fn read_property<T: RegisteredClass>(
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<T: RegisteredClass>(
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::<T>::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 props = T::get_metadata().get_properties();
let prop = props.get(prop_name.as_str()?);
// 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::<T>(object, member, type_, cache_slot, rv) {
Ok(rv) => rv,
Err(e) => {
let _ = e.throw();
(*rv).set_null();
rv
}
}
}
sourcepub fn set_resource(&mut self, val: *mut zend_resource)
pub fn set_resource(&mut self, val: *mut zend_resource)
sourcepub fn set_object(&mut self, val: &mut ZendObject)
pub fn set_object(&mut self, val: &mut ZendObject)
Sets the value of the zval as a reference to an object.
Parameters
val
- The value to set the zval as.
Examples found in repository?
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let obj = self.into_raw();
zv.set_object(&mut obj.std);
Ok(())
}
}
impl<T: RegisteredClass> IntoZval for &mut ZendClassObject<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.std);
Ok(())
}
More examples
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
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<'a> IntoZval for &'a mut ZendObject {
const TYPE: DataType = DataType::Object(None);
#[inline]
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
zv.set_object(self);
Ok(())
}
sourcepub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
&mut self,
val: T
) -> Result<()>
pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
&mut self,
val: T
) -> Result<()>
Sets the value of the zval as an array. Returns nothing in a result on success.
Parameters
val
- The value to set the zval as.
sourcepub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>)
pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>)
Sets the value of the zval as an array. Returns nothing in a result on success.
Parameters
val
- The value to set the zval as.
Examples found in repository?
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
zv.set_hashtable(self);
Ok(())
}
}
impl<'a> FromZval<'a> for &'a ZendHashTable {
const TYPE: DataType = DataType::Array;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array()
}
}
///////////////////////////////////////////
//// HashMap
///////////////////////////////////////////
impl<'a, V> TryFrom<&'a ZendHashTable> for HashMap<String, V>
where
V: FromZval<'a>,
{
type Error = Error;
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut hm = HashMap::with_capacity(value.len());
for (idx, key, val) in value.iter() {
hm.insert(
key.unwrap_or_else(|| idx.to_string()),
V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?,
);
}
Ok(hm)
}
}
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<ZendHashTable>
where
K: AsRef<str>,
V: IntoZval,
{
type Error = Error;
fn try_from(value: HashMap<K, V>) -> Result<Self> {
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)?;
}
Ok(ht)
}
}
impl<K, V> IntoZval for HashMap<K, V>
where
K: AsRef<str>,
V: IntoZval,
{
const TYPE: DataType = DataType::Array;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let arr = self.try_into()?;
zv.set_hashtable(arr);
Ok(())
}
}
impl<'a, T> FromZval<'a> for HashMap<String, T>
where
T: FromZval<'a>,
{
const TYPE: DataType = DataType::Array;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.array().and_then(|arr| arr.try_into().ok())
}
}
///////////////////////////////////////////
//// Vec
///////////////////////////////////////////
impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
where
T: FromZval<'a>,
{
type Error = Error;
fn try_from(value: &'a ZendHashTable) -> Result<Self> {
let mut vec = Vec::with_capacity(value.len());
for (_, _, val) in value.iter() {
vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?);
}
Ok(vec)
}
}
impl<T> TryFrom<Vec<T>> for ZBox<ZendHashTable>
where
T: IntoZval,
{
type Error = Error;
fn try_from(value: Vec<T>) -> Result<Self> {
let mut ht = ZendHashTable::with_capacity(
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
);
for val in value.into_iter() {
ht.push(val)?;
}
Ok(ht)
}
}
impl<T> IntoZval for Vec<T>
where
T: IntoZval,
{
const TYPE: DataType = DataType::Array;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
let arr = self.try_into()?;
zv.set_hashtable(arr);
Ok(())
}
More examples
sourcepub fn extract<'a, T>(&'a self) -> Option<T>where
T: FromZval<'a>,
pub fn extract<'a, T>(&'a self) -> Option<T>where
T: FromZval<'a>,
Extracts some type from a Zval
.
This is a wrapper function around TryFrom
.
Examples found in repository?
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
fn from_zend_object(obj: &ZendObject) -> Result<Self> {
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"),
);
}
}
sourcepub fn shallow_clone(&self) -> Zval
pub fn shallow_clone(&self) -> Zval
Creates a shallow clone of the Zval
.
This copies the contents of the Zval
, and increments the reference
counter of the underlying value (if it is reference counted).
For example, if the zval contains a long, it will simply copy the value. However, if the zval contains an object, the new zval will point to the same object, and the objects reference counter will be incremented.
Returns
The cloned zval.