Skip to content

Commit

Permalink
Merge pull request #268 from joelwurtz/feat/iterator
Browse files Browse the repository at this point in the history
feat(iterator): add helper for zend_object_iterator and iterable type
  • Loading branch information
danog authored Nov 24, 2023
2 parents b876077 + f449f4e commit 3d66f17
Show file tree
Hide file tree
Showing 15 changed files with 818 additions and 83 deletions.
2 changes: 2 additions & 0 deletions allowed_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ bind! {
zend_internal_arg_info,
zend_is_callable,
zend_is_identical,
zend_is_iterable,
zend_long,
zend_lookup_class_ex,
zend_module_entry,
Expand Down Expand Up @@ -163,6 +164,7 @@ bind! {
IS_UNDEF,
IS_VOID,
IS_PTR,
IS_ITERABLE,
MAY_BE_ANY,
MAY_BE_BOOL,
PHP_INI_USER,
Expand Down
4 changes: 4 additions & 0 deletions docsrs_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub const IS_RESOURCE: u32 = 9;
pub const IS_REFERENCE: u32 = 10;
pub const IS_CONSTANT_AST: u32 = 11;
pub const IS_CALLABLE: u32 = 12;
pub const IS_ITERABLE: u32 = 13;
pub const IS_VOID: u32 = 14;
pub const IS_MIXED: u32 = 16;
pub const IS_INDIRECT: u32 = 12;
Expand Down Expand Up @@ -1658,6 +1659,9 @@ extern "C" {
named_params: *mut HashTable,
);
}
extern "C" {
pub fn zend_is_iterable(iterable: *const zval) -> bool;
}
pub const _zend_expected_type_Z_EXPECTED_LONG: _zend_expected_type = 0;
pub const _zend_expected_type_Z_EXPECTED_LONG_OR_NULL: _zend_expected_type = 1;
pub const _zend_expected_type_Z_EXPECTED_BOOL: _zend_expected_type = 2;
Expand Down
55 changes: 55 additions & 0 deletions guide/src/types/iterable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# `Iterable`

`Iterable`s are represented either by an `array` or `Traversable` type.

| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation |
|---------------|----------------|-----------------| ---------------- |----------------------------------|
| Yes | No | No | No | `ZendHashTable` or `ZendIterator` |

Converting from a zval to a `Iterable` is valid when the value is either an array or an object
that implements the `Traversable` interface. This means that any value that can be used in a
`foreach` loop can be converted into a `Iterable`.

## Rust example

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::types::Iterable;
#[php_function]
pub fn test_iterable(mut iterable: Iterable) {
for (k, v) in iterable.iter().expect("cannot rewind iterator") {
println!("k: {} v: {}", k.string().unwrap(), v.string().unwrap());
}
}
# fn main() {}
```

## PHP example

```php
<?php

$generator = function() {
yield 'hello' => 'world';
yield 'rust' => 'php';
};

$array = [
'hello' => 'world',
'rust' => 'php',
];

test_iterable($generator());
test_iterable($array);
```

Output:

```text
k: hello v: world
k: rust v: php
k: hello v: world
k: rust v: php
```
52 changes: 52 additions & 0 deletions guide/src/types/iterator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# `ZendIterator`

`ZendIterator`s are represented by the `Traversable` type.

| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation |
|---------------| -------------- |-----------------| ---------------- | ------------------ |
| No | Yes | No | No | `ZendIterator` |

Converting from a zval to a `ZendIterator` is valid when there is an associated iterator to
the variable. This means that any value, at the exception of an `array`, that can be used in
a `foreach` loop can be converted into a `ZendIterator`. As an example, a `Generator` can be
used but also a the result of a `query` call with `PDO`.

If you want a more universal `iterable` type that also supports arrays, see [Iterable](./iterable.md).

## Rust example

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::types::ZendIterator;
#[php_function]
pub fn test_iterator(iterator: &mut ZendIterator) {
for (k, v) in iterator.iter().expect("cannot rewind iterator") {
// Note that the key can be anything, even an object
// when iterating over Traversables!
println!("k: {} v: {}", k.string().unwrap(), v.string().unwrap());
}
}
# fn main() {}
```

## PHP example

```php
<?php

$generator = function() {
yield 'hello' => 'world';
yield 'rust' => 'php';
};

test_iterator($generator());
```

Output:

```text
k: hello v: world
k: rust v: php
```
1 change: 1 addition & 0 deletions src/describe/stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ impl ToStub for DataType {
DataType::Reference => "reference",
DataType::Callable => "callable",
DataType::Bool => "bool",
DataType::Iterable => "iterable",
_ => "mixed",
}
)
Expand Down
12 changes: 8 additions & 4 deletions src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use crate::ffi::{
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR,
E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE,
E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING,
E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT, IS_LONG,
IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE,
IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR,
PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS,
E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT,
IS_ITERABLE, IS_LONG, IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE,
IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL,
PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS,
ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED,
ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING,
ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK,
Expand Down Expand Up @@ -49,6 +49,7 @@ bitflags! {
const ConstantExpression = IS_CONSTANT_AST;
const Void = IS_VOID;
const Ptr = IS_PTR;
const Iterable = IS_ITERABLE;

const InternedStringEx = Self::String.bits();
const StringEx = Self::String.bits() | Self::RefCounted.bits();
Expand Down Expand Up @@ -237,6 +238,7 @@ pub enum DataType {
Double,
String,
Array,
Iterable,
Object(Option<&'static str>),
Resource,
Reference,
Expand Down Expand Up @@ -277,6 +279,7 @@ impl DataType {
DataType::Mixed => IS_MIXED,
DataType::Bool => _IS_BOOL,
DataType::Ptr => IS_PTR,
DataType::Iterable => IS_ITERABLE,
}
}
}
Expand Down Expand Up @@ -383,6 +386,7 @@ impl Display for DataType {
DataType::Mixed => write!(f, "Mixed"),
DataType::Ptr => write!(f, "Pointer"),
DataType::Indirect => write!(f, "Indirect"),
DataType::Iterable => write!(f, "Iterable"),
}
}
}
Expand Down
Loading

0 comments on commit 3d66f17

Please sign in to comment.