|
| 1 | +# Zval |
| 2 | + |
| 3 | +> Refer to: <https://www.phpinternalsbook.com/php7/zvals/basic_structure.html#the-zval-struct> |
| 4 | +> |
| 5 | +> A zval (short for “Zend value”) represents an arbitrary PHP value. |
| 6 | +> As such it is likely the most important structure in all of PHP and |
| 7 | +> you’ll be working with it a lot. |
| 8 | +
|
| 9 | +The [`phper::values::ZVal`] is the wrapper of php zval. |
| 10 | + |
| 11 | +## Actual type of ZVal |
| 12 | + |
| 13 | +PHP is a dynamically typed language, zval can represent multiple types, |
| 14 | +but there is only one type at a time, you can use |
| 15 | +[`phper::values::ZVal::get_type_info`] to get the actual type. |
| 16 | + |
| 17 | +## Convert Rust type to ZVal |
| 18 | + |
| 19 | +The [`phper::values::ZVal`] implements a lot of [`std::convert::From`] for the |
| 20 | +conversion. |
| 21 | + |
| 22 | +Here is the mapping relationship of Rust type and base PHP type. |
| 23 | + |
| 24 | +| Trait | Rust type | PHP type | |
| 25 | +| --------------- | --------------------------- | -------- | |
| 26 | +| `From<()>` | `()` | null | |
| 27 | +| `From<bool>` | `bool` | bool | |
| 28 | +| `From<i64>` | `i64` | long | |
| 29 | +| `From<f64>` | `f64` | double | |
| 30 | +| `From<&str>` | `&str` | string | |
| 31 | +| `From<&CStr>` | `&CStr` | string | |
| 32 | +| `From<&[u8]>` | `&[u8]` | string | |
| 33 | +| `From<Vec<u8>>` | `Vec<u8>` | string | |
| 34 | +| `From<ZString>` | [`phper::strings::ZString`] | string | |
| 35 | +| `From<ZArray>` | [`phper::arrays::ZArray`] | array | |
| 36 | +| `From<ZObject>` | [`phper::objects::ZObject`] | object | |
| 37 | + |
| 38 | +Otherwise, there are also composite types that implement `From`. |
| 39 | + |
| 40 | +- `From<Option<T>>`: if Some(T), T will be converted to PHP type like `From<T>`, |
| 41 | + or `None` wll be converted to `null`. |
| 42 | + |
| 43 | +- `From<Result<T, E>>`: if Ok(T), T will be converted to PHP type like `From<T>`, |
| 44 | + or `Err(e)` will throw an Exception by calling `zend_throw_exception`. |
| 45 | + |
| 46 | +### Example |
| 47 | + |
| 48 | +```rust,no_run |
| 49 | +use phper::values::ZVal; |
| 50 | +
|
| 51 | +assert!(ZVal::from(()).get_type_info().is_null()); |
| 52 | +assert!(ZVal::from(100i64).get_type_info().is_long()); |
| 53 | +``` |
| 54 | + |
| 55 | +## Convert ZVal to Rust type |
| 56 | + |
| 57 | +Now you can use `as_*` or `expect_*` methods to convert ZVal to Rust types. |
| 58 | + |
| 59 | +- The `as_*` returns `Option<T>`. |
| 60 | + |
| 61 | +- The `expect_*` returns `phper::Result<T>`, if convert failed, |
| 62 | + [phper::errors::ExpectTypeError] will be returned, with the message: |
| 63 | + `type error: must be of type {expect_type}, {actual_type} given")`. |
| 64 | + |
| 65 | + |
| 66 | +### Example |
| 67 | + |
| 68 | +```rust,no_run |
| 69 | +use phper::echo; |
| 70 | +use phper::values::ZVal; |
| 71 | +
|
| 72 | +fn say_hello(arguments: &mut [ZVal]) -> phper::Result<()> { |
| 73 | + // Get the first argument, expect the type `ZStr`, and convert to Rust utf-8 |
| 74 | + // str. |
| 75 | + let name = arguments[0].expect_z_str()?.to_str()?; |
| 76 | +
|
| 77 | + // Macro which do php internal `echo`. |
| 78 | + echo!("Hello, {}!\n", name); |
| 79 | +
|
| 80 | + Ok(()) |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +## Value copy & reference counting copy |
| 85 | + |
| 86 | +The [`phper::values::ZVal`] both implements [`std::clone::Clone`] and |
| 87 | +[`phper::alloc::RefClone`]. |
| 88 | + |
| 89 | +- [`std::clone::Clone`]: represent value copy (Type ZObject is excluded because it is always passed by reference). |
| 90 | + |
| 91 | +- [`phper::alloc::RefClone`]: represent reference counting copy (Type (), bool, |
| 92 | + i64, f64 is excluded because they are not reference counting types). |
| 93 | + |
| 94 | +## PHP internal cast |
| 95 | + |
| 96 | +> Refer to: <https://www.phpinternalsbook.com/php7/zvals/casts_and_operations.html#casts> |
| 97 | +
|
| 98 | +PHP is a weakly typed language, supports type cast internally. |
| 99 | + |
| 100 | +The zend engine provides `convert_to_*` functions to do the type cast, and |
| 101 | +`ZVal` wraps them directly. |
| 102 | + |
| 103 | +## Callable |
| 104 | + |
| 105 | +The [`phper::values::ZVal`] can be call via [`phper::values::ZVal::call`], make |
| 106 | +sure the actual type is callable (string or array or closure). |
| 107 | + |
| 108 | +```rust,no_run |
| 109 | +use phper::values::ZVal; |
| 110 | +use phper::arrays::ZArray; |
| 111 | +
|
| 112 | +let mut arr = ZArray::new(); |
| 113 | +arr.insert("a", ZVal::from(1)); |
| 114 | +arr.insert("b", ZVal::from(2)); |
| 115 | +let ret = ZVal::from("json_encode").call(&mut [ZVal::from(arr)]).unwrap(); |
| 116 | +assert_eq!(ret.expect_z_str().unwrap().to_str(), Ok(r#"{"a":1,"b":2}"#)); |
| 117 | +``` |
0 commit comments