Skip to content

Commit 8df4123

Browse files
feat(extgen): add support for arrays as parameters and return types (#1724)
* feat(extgen): add support for arrays as parameters and return types * cs --------- Co-authored-by: Kévin Dunglas <[email protected]>
1 parent 1804e36 commit 8df4123

26 files changed

+1421
-565
lines changed

docs/extensions.md

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,77 @@ While the first point speaks for itself, the second may be harder to apprehend.
8181

8282
While some variable types have the same memory representation between C/PHP and Go, some types require more logic to be directly used. This is maybe the hardest part when it comes to writing extensions because it requires understanding internals of the Zend Engine and how variables are stored internally in PHP. This table summarizes what you need to know:
8383

84-
| PHP type | Go type | Direct conversion | C to Go helper | Go to C helper | Class Methods Support |
85-
|--------------------|------------------|-------------------|-----------------------|------------------------|-----------------------|
86-
| `int` | `int64` || - | - ||
87-
| `?int` | `*int64` || - | - ||
88-
| `float` | `float64` || - | - ||
89-
| `?float` | `*float64` || - | - ||
90-
| `bool` | `bool` || - | - ||
91-
| `?bool` | `*bool` || - | - ||
92-
| `string`/`?string` | `*C.zend_string` || frankenphp.GoString() | frankenphp.PHPString() ||
93-
| `array` | `slice`/`map` || _Not yet implemented_ | _Not yet implemented_ | |
94-
| `object` | `struct` || _Not yet implemented_ | _Not yet implemented_ ||
84+
| PHP type | Go type | Direct conversion | C to Go helper | Go to C helper | Class Methods Support |
85+
|--------------------|---------------------|-------------------|-----------------------|------------------------|-----------------------|
86+
| `int` | `int64` || - | - ||
87+
| `?int` | `*int64` || - | - ||
88+
| `float` | `float64` || - | - ||
89+
| `?float` | `*float64` || - | - ||
90+
| `bool` | `bool` || - | - ||
91+
| `?bool` | `*bool` || - | - ||
92+
| `string`/`?string` | `*C.zend_string` || frankenphp.GoString() | frankenphp.PHPString() ||
93+
| `array` | `*frankenphp.Array` || frankenphp.GoArray() | frankenphp.PHPArray() | |
94+
| `object` | `struct` || _Not yet implemented_ | _Not yet implemented_ ||
9595

9696
> [!NOTE]
9797
> This table is not exhaustive yet and will be completed as the FrankenPHP types API gets more complete.
9898
>
99-
> For class methods specifically, only primitive types are currently supported. Arrays and objects cannot be used as method parameters or return types yet.
99+
> For class methods specifically, primitive types and arrays are currently supported. Objects cannot be used as method parameters or return types yet.
100100
101101
If you refer to the code snippet of the previous section, you can see that helpers are used to convert the first parameter and the return value. The second and third parameter of our `repeat_this()` function don't need to be converted as memory representation of the underlying types are the same for both C and Go.
102102

103+
#### Working with Arrays
104+
105+
FrankenPHP provides native support for PHP arrays through the `frankenphp.Array` type. This type represents both PHP indexed arrays (lists) and associative arrays (hashmaps) with ordered key-value pairs.
106+
107+
**Creating and manipulating arrays in Go:**
108+
109+
```go
110+
//export_php:function process_data(array $input): array
111+
func process_data(arr *C.zval) unsafe.Pointer {
112+
// Convert PHP array to Go
113+
goArray := frankenphp.GoArray(unsafe.Pointer(arr))
114+
115+
result := &frankenphp.Array{}
116+
117+
result.SetInt(0, "first")
118+
result.SetInt(1, "second")
119+
result.Append("third") // Automatically assigns next integer key
120+
121+
result.SetString("name", "John")
122+
result.SetString("age", int64(30))
123+
124+
for i := uint32(0); i < goArray.Len(); i++ {
125+
key, value := goArray.At(i)
126+
if key.Type == frankenphp.PHPStringKey {
127+
result.SetString("processed_"+key.Str, value)
128+
} else {
129+
result.SetInt(key.Int+100, value)
130+
}
131+
}
132+
133+
// Convert back to PHP array
134+
return frankenphp.PHPArray(result)
135+
}
136+
```
137+
138+
**Key features of `frankenphp.Array`:**
139+
140+
* **Ordered key-value pairs** - Maintains insertion order like PHP arrays
141+
* **Mixed key types** - Supports both integer and string keys in the same array
142+
* **Type safety** - The `PHPKey` type ensures proper key handling
143+
* **Automatic list detection** - When converting to PHP, automatically detects if array should be a packed list or hashmap
144+
* **Objects are not supported** - Currently, only scalar types and arrays can be used as values. Providing an object will result in a `null` value in the PHP array.
145+
146+
**Available methods:**
147+
148+
* `SetInt(key int64, value interface{})` - Set value with integer key
149+
* `SetString(key string, value interface{})` - Set value with string key
150+
* `Append(value interface{})` - Add value with next available integer key
151+
* `Len() uint32` - Get number of elements
152+
* `At(index uint32) (PHPKey, interface{})` - Get key-value pair at index
153+
* `frankenphp.PHPArray(arr *frankenphp.Array) unsafe.Pointer` - Convert to PHP array
154+
103155
### Declaring a Native PHP Class
104156

105157
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:
@@ -188,7 +240,7 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool)
188240
* **PHP `null` becomes Go `nil`** - when PHP passes `null`, your Go function receives a `nil` pointer
189241

190242
> [!WARNING]
191-
> Currently, class methods have the following limitations. **Arrays and objects are not supported** as parameter types or return types. Only scalar types are supported: `string`, `int`, `float`, `bool` and `void` (for return type). **Nullable parameter types are fully supported** for all scalar types (`?string`, `?int`, `?float`, `?bool`).
243+
> Currently, class methods have the following limitations. **Objects are not supported** as parameter types or return types. **Arrays are fully supported** for both parameters and return types. Supported types: `string`, `int`, `float`, `bool`, `array`, and `void` (for return type). **Nullable parameter types are fully supported** for all scalar types (`?string`, `?int`, `?float`, `?bool`).
192244
193245
After generating the extension, you will be allowed to use the class and its methods in PHP. Note that you **cannot access properties directly**:
194246

docs/fr/extensions.md

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,77 @@ Alors que le premier point parle de lui-même, le second peut être plus diffici
8181

8282
Bien que certains types de variables aient la même représentation mémoire entre C/PHP et Go, certains types nécessitent plus de logique pour être directement utilisés. C'est peut-être la partie la plus difficile quand il s'agit d'écrire des extensions car cela nécessite de comprendre les fonctionnements internes du moteur Zend et comment les variables sont stockées dans le moteur de PHP. Ce tableau résume ce que vous devez savoir :
8383

84-
| Type PHP | Type Go | Conversion directe | Assistant C vers Go | Assistant Go vers C | Support des Méthodes de Classe |
85-
|--------------------|------------------|--------------------|-------------------------|-------------------------|--------------------------------|
86-
| `int` | `int64` || - | - ||
87-
| `?int` | `*int64` || - | - ||
88-
| `float` | `float64` || - | - ||
89-
| `?float` | `*float64` || - | - ||
90-
| `bool` | `bool` || - | - ||
91-
| `?bool` | `*bool` || - | - ||
92-
| `string`/`?string` | `*C.zend_string` || frankenphp.GoString() | frankenphp.PHPString() ||
93-
| `array` | `slice`/`map` || _Pas encore implémenté_ | _Pas encore implémenté_ | |
94-
| `object` | `struct` || _Pas encore implémenté_ | _Pas encore implémenté_ ||
84+
| Type PHP | Type Go | Conversion directe | Assistant C vers Go | Assistant Go vers C | Support des Méthodes de Classe |
85+
|--------------------|---------------------|--------------------|-------------------------|-------------------------|--------------------------------|
86+
| `int` | `int64` || - | - ||
87+
| `?int` | `*int64` || - | - ||
88+
| `float` | `float64` || - | - ||
89+
| `?float` | `*float64` || - | - ||
90+
| `bool` | `bool` || - | - ||
91+
| `?bool` | `*bool` || - | - ||
92+
| `string`/`?string` | `*C.zend_string` || frankenphp.GoString() | frankenphp.PHPString() ||
93+
| `array` | `*frankenphp.Array` || frankenphp.GoArray() | frankenphp.PHPArray() | |
94+
| `object` | `struct` || _Pas encore implémenté_ | _Pas encore implémenté_ ||
9595

9696
> [!NOTE]
9797
> Ce tableau n'est pas encore exhaustif et sera complété au fur et à mesure que l'API de types FrankenPHP deviendra plus complète.
9898
>
99-
> Pour les méthodes de classe spécifiquement, seuls les types primitifs sont actuellement supportés. Les tableaux et objets ne peuvent pas encore être utilisés comme paramètres de méthode ou types de retour.
99+
> Pour les méthodes de classe spécifiquement, les types primitifs et les tableaux sont supportés. Les objets ne peuvent pas encore être utilisés comme paramètres de méthode ou types de retour.
100100
101101
Si vous vous référez à l'extrait de code de la section précédente, vous pouvez voir que des assistants sont utilisés pour convertir le premier paramètre et la valeur de retour. Les deuxième et troisième paramètres de notre fonction `repeat_this()` n'ont pas besoin d'être convertis car la représentation mémoire des types sous-jacents est la même pour C et Go.
102102

103+
#### Travailler avec les Tableaux
104+
105+
FrankenPHP fournit un support natif pour les tableaux PHP à travers le type `frankenphp.Array`. Ce type représente à la fois les tableaux indexés PHP (listes) et les tableaux associatifs (hashmaps) avec des paires clé-valeur ordonnées.
106+
107+
**Créer et manipuler des tableaux en Go :**
108+
109+
```go
110+
//export_php:function process_data(array $input): array
111+
func process_data(arr *C.zval) unsafe.Pointer {
112+
// Convertir le tableau PHP vers Go
113+
goArray := frankenphp.GoArray(unsafe.Pointer(arr))
114+
115+
result := &frankenphp.Array{}
116+
117+
result.SetInt(0, "first")
118+
result.SetInt(1, "second")
119+
result.Append("third") // Assigne automatiquement la prochaine clé entière
120+
121+
result.SetString("name", "John")
122+
result.SetString("age", int64(30))
123+
124+
for i := uint32(0); i < goArray.Len(); i++ {
125+
key, value := goArray.At(i)
126+
if key.Type == frankenphp.PHPStringKey {
127+
result.SetString("processed_"+key.Str, value)
128+
} else {
129+
result.SetInt(key.Int+100, value)
130+
}
131+
}
132+
133+
// Reconvertir vers un tableau PHP
134+
return frankenphp.PHPArray(result)
135+
}
136+
```
137+
138+
**Fonctionnalités clés de `frankenphp.Array` :**
139+
140+
* **Paires clé-valeur ordonnées** - Maintient l'ordre d'insertion comme les tableaux PHP
141+
* **Types de clés mixtes** - Supporte les clés entières et chaînes dans le même tableau
142+
* **Sécurité de type** - Le type `PHPKey` assure une gestion appropriée des clés
143+
* **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste compacte ou un hashmap
144+
* **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux sont supportés. Passer un objet en tant qu'élément du tableau résultera d'une valeur `null` dans le tableau PHP.
145+
146+
**Méthodes disponibles :**
147+
148+
* `SetInt(key int64, value interface{})` - Définir une valeur avec une clé entière
149+
* `SetString(key string, value interface{})` - Définir une valeur avec une clé chaîne
150+
* `Append(value interface{})` - Ajouter une valeur avec la prochaine clé entière disponible
151+
* `Len() uint32` - Obtenir le nombre d'éléments
152+
* `At(index uint32) (PHPKey, interface{})` - Obtenir la paire clé-valeur à l'index
153+
* `frankenphp.PHPArray(arr *frankenphp.Array) unsafe.Pointer` - Convertir vers un tableau PHP
154+
103155
### Déclarer une Classe PHP Native
104156

105157
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 :
@@ -188,7 +240,7 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool)
188240
* **PHP `null` devient Go `nil`** - quand PHP passe `null`, votre fonction Go reçoit un pointeur `nil`
189241

190242
> [!WARNING]
191-
> Actuellement, les méthodes de classe ont les limitations suivantes. **Les tableaux et objets ne sont pas supportés** comme types de paramètres ou types de retour. Seuls les types scalaires sont supportés : `string`, `int`, `float`, `bool` et `void` (pour le type de retour). **Les types de paramètres nullables sont entièrement supportés** pour tous les types scalaires (`?string`, `?int`, `?float`, `?bool`).
243+
> Actuellement, les méthodes de classe ont les limitations suivantes. **Les objets ne sont pas supportés** comme types de paramètres ou types de retour. **Les tableaux sont entièrement supportés** pour les paramètres et types de retour. Types supportés : `string`, `int`, `float`, `bool`, `array`, et `void` (pour le type de retour). **Les types de paramètres nullables sont entièrement supportés** pour tous les types scalaires (`?string`, `?int`, `?float`, `?bool`).
192244
193245
Après avoir généré l'extension, vous serez autorisé à utiliser la classe et ses méthodes en PHP. Notez que vous **ne pouvez pas accéder aux propriétés directement** :
194246

0 commit comments

Comments
 (0)