feat(extgen): add support for callable in parameters (#1731)

This commit is contained in:
Alexandre Daubois
2025-12-15 12:50:50 +01:00
committed by GitHub
parent 58a63703b4
commit bb1c3678dc
17 changed files with 743 additions and 77 deletions

View File

@@ -88,19 +88,20 @@ While some variable types have the same memory representation between C/PHP and
This table summarizes what you need to know:
| PHP type | Go type | Direct conversion | C to Go helper | Go to C helper | Class Methods Support |
| ------------------ | ----------------------------- | ----------------- | --------------------------------- | ---------------------------------- | --------------------- |
| `int` | `int64` | ✅ | - | - | ✅ |
| `?int` | `*int64` | ✅ | - | - | ✅ |
| `float` | `float64` | ✅ | - | - | ✅ |
| `?float` | `*float64` | ✅ | - | - | ✅ |
| `bool` | `bool` | ✅ | - | - | ✅ |
| `?bool` | `*bool` | ✅ | - | - | ✅ |
| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ |
| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ |
| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ |
| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ |
| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ |
| `object` | `struct` | ❌ | _Not yet implemented_ | _Not yet implemented_ | ❌ |
|--------------------|-------------------------------|-------------------|-----------------------------------|------------------------------------|-----------------------|
| `int` | `int64` | ✅ | - | - | ✅ |
| `?int` | `*int64` | ✅ | - | - | ✅ |
| `float` | `float64` | ✅ | - | - | ✅ |
| `?float` | `*float64` | ✅ | - | - | ✅ |
| `bool` | `bool` | ✅ | - | - | ✅ |
| `?bool` | `*bool` | ✅ | - | - | ✅ |
| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ |
| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ |
| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ |
| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ |
| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ |
| `callable` | `*C.zval` | ❌ | - | frankenphp.CallPHPCallable() | ❌ |
| `object` | `struct` | ❌ | _Not yet implemented_ | _Not yet implemented_ | ❌ |
> [!NOTE]
>
@@ -212,6 +213,42 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer {
- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convert a PHP array to an unordered Go map
- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convert a PHP array to a Go slice
### Working with Callables
FrankenPHP provides a way to work with PHP callables using the `frankenphp.CallPHPCallable` helper. This allows you to call PHP functions or methods from Go code.
To showcase this, let's create our own `array_map()` function that takes a callable and an array, applies the callable to each element of the array, and returns a new array with the results:
```go
// export_php:function my_array_map(array $data, callable $callback): array
func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer {
goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr))
if err != nil {
panic(err)
}
result := make([]any, len(goSlice))
for index, value := range goSlice {
result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value})
}
return frankenphp.PHPPackedArray(result)
}
```
Notice how we use `frankenphp.CallPHPCallable()` to call the PHP callable passed as a parameter. This function takes a pointer to the callable and an array of arguments, and it returns the result of the callable execution. You can use the callable syntax you're used to:
```php
<?php
$result = my_array_map([1, 2, 3], function($x) { return $x * 2; });
// $result will be [2, 4, 6]
$result = my_array_map(['hello', 'world'], 'strtoupper');
// $result will be ['HELLO', 'WORLD']
```
### Declaring a Native PHP Class
The generator supports declaring **opaque classes** as Go structs, which can be used to create PHP objects. You can use the `//export_php:class` directive comment to define a PHP class. For example:

View File

@@ -210,6 +210,42 @@ func process_data_packed(arr *C.zval) unsafe.Pointer {
- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convertir un tableau PHP vers une map Go non ordonnée
- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convertir un tableau PHP vers un slice Go
### Travailler avec des Callables
FrankenPHP propose un moyen de travailler avec les _callables_ PHP grâce au helper `frankenphp.CallPHPCallable()`. Cela permet d'appeler des fonctions ou des méthodes PHP depuis du code Go.
Pour illustrer cela, créons notre propre fonction `array_map()` qui prend un _callable_ et un tableau, applique le _callable_ à chaque élément du tableau, et retourne un nouveau tableau avec les résultats :
```go
// export_php:function my_array_map(array $data, callable $callback): array
func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer {
goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr))
if err != nil {
panic(err)
}
result := make([]any, len(goSlice))
for index, value := range goSlice {
result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value})
}
return frankenphp.PHPPackedArray(result)
}
```
Remarquez comment nous utilisons `frankenphp.CallPHPCallable()` pour appeler le _callable_ PHP passé en paramètre. Cette fonction prend un pointeur vers le _callable_ et un tableau d'arguments, et elle retourne le résultat de l'exécution du _callable_. Vous pouvez utiliser la syntaxe habituelle des _callables_ :
```php
<?php
$result = my_array_map([1, 2, 3], function($x) { return $x * 2; });
// $result vaudra [2, 4, 6]
$result = my_array_map(['hello', 'world'], 'strtoupper');
// $result vaudra ['HELLO', 'WORLD']
```
### Déclarer une Classe PHP Native
Le générateur prend en charge la déclaration de **classes opaques** comme structures Go, qui peuvent être utilisées pour créer des objets PHP. Vous pouvez utiliser la directive `//export_php:class` pour définir une classe PHP. Par exemple :