11//! Represents an array in PHP. As all arrays in PHP are associative arrays,
22//! they are represented by hash tables.
33
4- use std:: {
5- collections:: HashMap ,
6- convert:: { TryFrom , TryInto } ,
7- ffi:: CString ,
8- fmt:: { Debug , Display } ,
9- iter:: FromIterator ,
10- ptr,
11- } ;
12-
4+ use crate :: ffi:: zend_ulong;
135use crate :: {
146 boxed:: { ZBox , ZBoxable } ,
157 convert:: { FromZval , IntoZval } ,
@@ -25,6 +17,15 @@ use crate::{
2517 flags:: DataType ,
2618 types:: Zval ,
2719} ;
20+ use std:: str:: FromStr ;
21+ use std:: {
22+ collections:: HashMap ,
23+ convert:: { TryFrom , TryInto } ,
24+ ffi:: CString ,
25+ fmt:: { Debug , Display } ,
26+ iter:: FromIterator ,
27+ ptr,
28+ } ;
2829
2930/// A PHP hashtable.
3031///
@@ -188,9 +189,39 @@ impl ZendHashTable {
188189 /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
189190 /// ```
190191 #[ must_use]
191- pub fn get ( & self , key : & ' _ str ) -> Option < & Zval > {
192- let str = CString :: new ( key) . ok ( ) ?;
193- unsafe { zend_hash_str_find ( self , str. as_ptr ( ) , key. len ( ) as _ ) . as_ref ( ) }
192+ pub fn get < ' a , K > ( & self , key : K ) -> Option < & Zval >
193+ where
194+ K : Into < ArrayKey < ' a > > ,
195+ {
196+ match key. into ( ) {
197+ ArrayKey :: Long ( index) => unsafe {
198+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
199+ } ,
200+ ArrayKey :: String ( key) => {
201+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
202+ unsafe { zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( ) }
203+ } else {
204+ unsafe {
205+ zend_hash_str_find (
206+ self ,
207+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
208+ key. len ( ) as _ ,
209+ )
210+ . as_ref ( )
211+ }
212+ }
213+ }
214+ ArrayKey :: Str ( key) => {
215+ if let Ok ( index) = i64:: from_str ( key) {
216+ unsafe { zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( ) }
217+ } else {
218+ unsafe {
219+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
220+ . as_ref ( )
221+ }
222+ }
223+ }
224+ }
194225 }
195226
196227 /// Attempts to retrieve a value from the hash table with a string key.
@@ -216,9 +247,39 @@ impl ZendHashTable {
216247 /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
217248 /// ```
218249 #[ must_use]
219- pub fn get_mut ( & self , key : & ' _ str ) -> Option < & mut Zval > {
220- let str = CString :: new ( key) . ok ( ) ?;
221- unsafe { zend_hash_str_find ( self , str. as_ptr ( ) , key. len ( ) as _ ) . as_mut ( ) }
250+ pub fn get_mut < ' a , K > ( & self , key : K ) -> Option < & mut Zval >
251+ where
252+ K : Into < ArrayKey < ' a > > ,
253+ {
254+ match key. into ( ) {
255+ ArrayKey :: Long ( index) => unsafe {
256+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
257+ } ,
258+ ArrayKey :: String ( key) => {
259+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
260+ unsafe { zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( ) }
261+ } else {
262+ unsafe {
263+ zend_hash_str_find (
264+ self ,
265+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
266+ key. len ( ) as _ ,
267+ )
268+ . as_mut ( )
269+ }
270+ }
271+ }
272+ ArrayKey :: Str ( key) => {
273+ if let Ok ( index) = i64:: from_str ( key) {
274+ unsafe { zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( ) }
275+ } else {
276+ unsafe {
277+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
278+ . as_mut ( )
279+ }
280+ }
281+ }
282+ }
222283 }
223284
224285 /// Attempts to retrieve a value from the hash table with an index.
@@ -244,8 +305,8 @@ impl ZendHashTable {
244305 /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
245306 /// ```
246307 #[ must_use]
247- pub fn get_index ( & self , key : u64 ) -> Option < & Zval > {
248- unsafe { zend_hash_index_find ( self , key) . as_ref ( ) }
308+ pub fn get_index ( & self , key : i64 ) -> Option < & Zval > {
309+ unsafe { zend_hash_index_find ( self , key as zend_ulong ) . as_ref ( ) }
249310 }
250311
251312 /// Attempts to retrieve a value from the hash table with an index.
@@ -271,8 +332,8 @@ impl ZendHashTable {
271332 /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
272333 /// ```
273334 #[ must_use]
274- pub fn get_index_mut ( & self , key : u64 ) -> Option < & mut Zval > {
275- unsafe { zend_hash_index_find ( self , key) . as_mut ( ) }
335+ pub fn get_index_mut ( & self , key : i64 ) -> Option < & mut Zval > {
336+ unsafe { zend_hash_index_find ( self , key as zend_ulong ) . as_mut ( ) }
276337 }
277338
278339 /// Attempts to remove a value from the hash table with a string key.
@@ -299,9 +360,35 @@ impl ZendHashTable {
299360 /// ht.remove("test");
300361 /// assert_eq!(ht.len(), 0);
301362 /// ```
302- pub fn remove ( & mut self , key : & str ) -> Option < ( ) > {
303- let result =
304- unsafe { zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ ) } ;
363+ pub fn remove < ' a , K > ( & mut self , key : K ) -> Option < ( ) >
364+ where
365+ K : Into < ArrayKey < ' a > > ,
366+ {
367+ let result = match key. into ( ) {
368+ ArrayKey :: Long ( index) => unsafe { zend_hash_index_del ( self , index as zend_ulong ) } ,
369+ ArrayKey :: String ( key) => {
370+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
371+ unsafe { zend_hash_index_del ( self , index as zend_ulong ) }
372+ } else {
373+ unsafe {
374+ zend_hash_str_del (
375+ self ,
376+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
377+ key. len ( ) as _ ,
378+ )
379+ }
380+ }
381+ }
382+ ArrayKey :: Str ( key) => {
383+ if let Ok ( index) = i64:: from_str ( key) {
384+ unsafe { zend_hash_index_del ( self , index as zend_ulong ) }
385+ } else {
386+ unsafe {
387+ zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
388+ }
389+ }
390+ }
391+ } ;
305392
306393 if result < 0 {
307394 None
@@ -334,8 +421,8 @@ impl ZendHashTable {
334421 /// ht.remove_index(0);
335422 /// assert_eq!(ht.len(), 0);
336423 /// ```
337- pub fn remove_index ( & mut self , key : u64 ) -> Option < ( ) > {
338- let result = unsafe { zend_hash_index_del ( self , key) } ;
424+ pub fn remove_index ( & mut self , key : i64 ) -> Option < ( ) > {
425+ let result = unsafe { zend_hash_index_del ( self , key as zend_ulong ) } ;
339426
340427 if result < 0 {
341428 None
@@ -373,12 +460,45 @@ impl ZendHashTable {
373460 /// ht.insert("c", "C");
374461 /// assert_eq!(ht.len(), 3);
375462 /// ```
376- pub fn insert < V > ( & mut self , key : & str , val : V ) -> Result < ( ) >
463+ pub fn insert < ' a , K , V > ( & mut self , key : K , val : V ) -> Result < ( ) >
377464 where
465+ K : Into < ArrayKey < ' a > > ,
378466 V : IntoZval ,
379467 {
380468 let mut val = val. into_zval ( false ) ?;
381- unsafe { zend_hash_str_update ( self , CString :: new ( key) ?. as_ptr ( ) , key. len ( ) , & raw mut val) } ;
469+ match key. into ( ) {
470+ ArrayKey :: Long ( index) => {
471+ unsafe { zend_hash_index_update ( self , index as zend_ulong , & raw mut val) } ;
472+ }
473+ ArrayKey :: String ( key) => {
474+ if let Ok ( idx) = i64:: from_str ( & key) {
475+ unsafe { zend_hash_index_update ( self , idx as zend_ulong , & mut val) } ;
476+ } else {
477+ unsafe {
478+ zend_hash_str_update (
479+ self ,
480+ CString :: new ( key. as_str ( ) ) ?. as_ptr ( ) ,
481+ key. len ( ) ,
482+ & raw mut val,
483+ )
484+ } ;
485+ }
486+ }
487+ ArrayKey :: Str ( key) => {
488+ if let Ok ( idx) = i64:: from_str ( key) {
489+ unsafe { zend_hash_index_update ( self , idx as zend_ulong , & mut val) } ;
490+ } else {
491+ unsafe {
492+ zend_hash_str_update (
493+ self ,
494+ CString :: new ( key) ?. as_ptr ( ) ,
495+ key. len ( ) ,
496+ & raw mut val,
497+ )
498+ } ;
499+ }
500+ }
501+ }
382502 val. release ( ) ;
383503 Ok ( ( ) )
384504 }
@@ -411,12 +531,12 @@ impl ZendHashTable {
411531 /// ht.insert_at_index(0, "C"); // notice overriding index 0
412532 /// assert_eq!(ht.len(), 2);
413533 /// ```
414- pub fn insert_at_index < V > ( & mut self , key : u64 , val : V ) -> Result < ( ) >
534+ pub fn insert_at_index < V > ( & mut self , key : i64 , val : V ) -> Result < ( ) >
415535 where
416536 V : IntoZval ,
417537 {
418538 let mut val = val. into_zval ( false ) ?;
419- unsafe { zend_hash_index_update ( self , key, & raw mut val) } ;
539+ unsafe { zend_hash_index_update ( self , key as zend_ulong , & raw mut val) } ;
420540 val. release ( ) ;
421541 Ok ( ( ) )
422542 }
@@ -554,6 +674,8 @@ impl ZendHashTable {
554674 /// }
555675 /// ArrayKey::String(key) => {
556676 /// }
677+ /// ArrayKey::Str(key) => {
678+ /// }
557679 /// }
558680 /// dbg!(key, val);
559681 /// }
@@ -606,15 +728,23 @@ pub struct Iter<'a> {
606728}
607729
608730/// Represents the key of a PHP array, which can be either a long or a string.
609- #[ derive( Debug , PartialEq ) ]
610- pub enum ArrayKey {
731+ #[ derive( Debug , Clone , PartialEq ) ]
732+ pub enum ArrayKey < ' a > {
611733 /// A numerical key.
612734 Long ( i64 ) ,
613735 /// A string key.
614736 String ( String ) ,
737+ /// A string key by reference.
738+ Str ( & ' a str ) ,
739+ }
740+
741+ impl From < String > for ArrayKey < ' _ > {
742+ fn from ( value : String ) -> Self {
743+ Self :: String ( value)
744+ }
615745}
616746
617- impl ArrayKey {
747+ impl ArrayKey < ' _ > {
618748 /// Check if the key is an integer.
619749 ///
620750 /// # Returns
@@ -625,20 +755,34 @@ impl ArrayKey {
625755 match self {
626756 ArrayKey :: Long ( _) => true ,
627757 ArrayKey :: String ( _) => false ,
758+ ArrayKey :: Str ( _) => false ,
628759 }
629760 }
630761}
631762
632- impl Display for ArrayKey {
763+ impl Display for ArrayKey < ' _ > {
633764 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
634765 match self {
635766 ArrayKey :: Long ( key) => write ! ( f, "{key}" ) ,
636767 ArrayKey :: String ( key) => write ! ( f, "{key}" ) ,
768+ ArrayKey :: Str ( key) => write ! ( f, "{key}" ) ,
637769 }
638770 }
639771}
640772
641- impl < ' a > FromZval < ' a > for ArrayKey {
773+ impl < ' a > From < & ' a str > for ArrayKey < ' a > {
774+ fn from ( key : & ' a str ) -> ArrayKey < ' a > {
775+ ArrayKey :: Str ( key)
776+ }
777+ }
778+
779+ impl < ' a > From < i64 > for ArrayKey < ' a > {
780+ fn from ( index : i64 ) -> ArrayKey < ' a > {
781+ ArrayKey :: Long ( index)
782+ }
783+ }
784+
785+ impl < ' a > FromZval < ' a > for ArrayKey < ' _ > {
642786 const TYPE : DataType = DataType :: String ;
643787
644788 fn from_zval ( zval : & ' a Zval ) -> Option < Self > {
@@ -680,7 +824,7 @@ impl<'a> Iter<'a> {
680824}
681825
682826impl < ' a > IntoIterator for & ' a ZendHashTable {
683- type Item = ( ArrayKey , & ' a Zval ) ;
827+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
684828 type IntoIter = Iter < ' a > ;
685829
686830 /// Returns an iterator over the key(s) and value contained inside the
@@ -707,7 +851,7 @@ impl<'a> IntoIterator for &'a ZendHashTable {
707851}
708852
709853impl < ' a > Iterator for Iter < ' a > {
710- type Item = ( ArrayKey , & ' a Zval ) ;
854+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
711855
712856 fn next ( & mut self ) -> Option < Self :: Item > {
713857 self . next_zval ( )
@@ -1046,8 +1190,8 @@ impl FromIterator<Zval> for ZBox<ZendHashTable> {
10461190 }
10471191}
10481192
1049- impl FromIterator < ( u64 , Zval ) > for ZBox < ZendHashTable > {
1050- fn from_iter < T : IntoIterator < Item = ( u64 , Zval ) > > ( iter : T ) -> Self {
1193+ impl FromIterator < ( i64 , Zval ) > for ZBox < ZendHashTable > {
1194+ fn from_iter < T : IntoIterator < Item = ( i64 , Zval ) > > ( iter : T ) -> Self {
10511195 let mut ht = ZendHashTable :: new ( ) ;
10521196 for ( key, val) in iter {
10531197 // Inserting a zval cannot fail, as `push` only returns `Err` if converting
0 commit comments