@@ -8,12 +8,14 @@ use std::{
88 fmt:: { Debug , Display } ,
99 iter:: FromIterator ,
1010 ptr,
11+ str:: FromStr ,
1112} ;
1213
1314use crate :: {
1415 boxed:: { ZBox , ZBoxable } ,
1516 convert:: { FromZval , IntoZval } ,
1617 error:: { Error , Result } ,
18+ ffi:: zend_ulong,
1719 ffi:: {
1820 _zend_new_array, zend_array_count, zend_array_destroy, zend_array_dup, zend_hash_clean,
1921 zend_hash_get_current_data_ex, zend_hash_get_current_key_type_ex,
@@ -188,9 +190,46 @@ impl ZendHashTable {
188190 /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
189191 /// ```
190192 #[ 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 ( ) }
193+ pub fn get < ' a , K > ( & self , key : K ) -> Option < & Zval >
194+ where
195+ K : Into < ArrayKey < ' a > > ,
196+ {
197+ match key. into ( ) {
198+ ArrayKey :: Long ( index) => unsafe {
199+ #[ allow( clippy:: cast_sign_loss) ]
200+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
201+ } ,
202+ ArrayKey :: String ( key) => {
203+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
204+ #[ allow( clippy:: cast_sign_loss) ]
205+ unsafe {
206+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
207+ }
208+ } else {
209+ unsafe {
210+ zend_hash_str_find (
211+ self ,
212+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
213+ key. len ( ) as _ ,
214+ )
215+ . as_ref ( )
216+ }
217+ }
218+ }
219+ ArrayKey :: Str ( key) => {
220+ if let Ok ( index) = i64:: from_str ( key) {
221+ #[ allow( clippy:: cast_sign_loss) ]
222+ unsafe {
223+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
224+ }
225+ } else {
226+ unsafe {
227+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
228+ . as_ref ( )
229+ }
230+ }
231+ }
232+ }
194233 }
195234
196235 /// Attempts to retrieve a value from the hash table with a string key.
@@ -219,9 +258,46 @@ impl ZendHashTable {
219258 // hashtable while only having a reference to it. #461
220259 #[ allow( clippy:: mut_from_ref) ]
221260 #[ must_use]
222- pub fn get_mut ( & self , key : & ' _ str ) -> Option < & mut Zval > {
223- let str = CString :: new ( key) . ok ( ) ?;
224- unsafe { zend_hash_str_find ( self , str. as_ptr ( ) , key. len ( ) as _ ) . as_mut ( ) }
261+ pub fn get_mut < ' a , K > ( & self , key : K ) -> Option < & mut Zval >
262+ where
263+ K : Into < ArrayKey < ' a > > ,
264+ {
265+ match key. into ( ) {
266+ ArrayKey :: Long ( index) => unsafe {
267+ #[ allow( clippy:: cast_sign_loss) ]
268+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
269+ } ,
270+ ArrayKey :: String ( key) => {
271+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
272+ #[ allow( clippy:: cast_sign_loss) ]
273+ unsafe {
274+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
275+ }
276+ } else {
277+ unsafe {
278+ zend_hash_str_find (
279+ self ,
280+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
281+ key. len ( ) as _ ,
282+ )
283+ . as_mut ( )
284+ }
285+ }
286+ }
287+ ArrayKey :: Str ( key) => {
288+ if let Ok ( index) = i64:: from_str ( key) {
289+ #[ allow( clippy:: cast_sign_loss) ]
290+ unsafe {
291+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
292+ }
293+ } else {
294+ unsafe {
295+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
296+ . as_mut ( )
297+ }
298+ }
299+ }
300+ }
225301 }
226302
227303 /// Attempts to retrieve a value from the hash table with an index.
@@ -247,8 +323,11 @@ impl ZendHashTable {
247323 /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
248324 /// ```
249325 #[ must_use]
250- pub fn get_index ( & self , key : u64 ) -> Option < & Zval > {
251- unsafe { zend_hash_index_find ( self , key) . as_ref ( ) }
326+ pub fn get_index ( & self , key : i64 ) -> Option < & Zval > {
327+ #[ allow( clippy:: cast_sign_loss) ]
328+ unsafe {
329+ zend_hash_index_find ( self , key as zend_ulong ) . as_ref ( )
330+ }
252331 }
253332
254333 /// Attempts to retrieve a value from the hash table with an index.
@@ -277,8 +356,11 @@ impl ZendHashTable {
277356 // hashtable while only having a reference to it. #461
278357 #[ allow( clippy:: mut_from_ref) ]
279358 #[ must_use]
280- pub fn get_index_mut ( & self , key : u64 ) -> Option < & mut Zval > {
281- unsafe { zend_hash_index_find ( self , key) . as_mut ( ) }
359+ pub fn get_index_mut ( & self , key : i64 ) -> Option < & mut Zval > {
360+ unsafe {
361+ #[ allow( clippy:: cast_sign_loss) ]
362+ zend_hash_index_find ( self , key as zend_ulong ) . as_mut ( )
363+ }
282364 }
283365
284366 /// Attempts to remove a value from the hash table with a string key.
@@ -305,9 +387,44 @@ impl ZendHashTable {
305387 /// ht.remove("test");
306388 /// assert_eq!(ht.len(), 0);
307389 /// ```
308- pub fn remove ( & mut self , key : & str ) -> Option < ( ) > {
309- let result =
310- unsafe { zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ ) } ;
390+ pub fn remove < ' a , K > ( & mut self , key : K ) -> Option < ( ) >
391+ where
392+ K : Into < ArrayKey < ' a > > ,
393+ {
394+ let result = match key. into ( ) {
395+ ArrayKey :: Long ( index) => unsafe {
396+ #[ allow( clippy:: cast_sign_loss) ]
397+ zend_hash_index_del ( self , index as zend_ulong )
398+ } ,
399+ ArrayKey :: String ( key) => {
400+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
401+ #[ allow( clippy:: cast_sign_loss) ]
402+ unsafe {
403+ zend_hash_index_del ( self , index as zend_ulong )
404+ }
405+ } else {
406+ unsafe {
407+ zend_hash_str_del (
408+ self ,
409+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
410+ key. len ( ) as _ ,
411+ )
412+ }
413+ }
414+ }
415+ ArrayKey :: Str ( key) => {
416+ if let Ok ( index) = i64:: from_str ( key) {
417+ #[ allow( clippy:: cast_sign_loss) ]
418+ unsafe {
419+ zend_hash_index_del ( self , index as zend_ulong )
420+ }
421+ } else {
422+ unsafe {
423+ zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
424+ }
425+ }
426+ }
427+ } ;
311428
312429 if result < 0 {
313430 None
@@ -340,8 +457,11 @@ impl ZendHashTable {
340457 /// ht.remove_index(0);
341458 /// assert_eq!(ht.len(), 0);
342459 /// ```
343- pub fn remove_index ( & mut self , key : u64 ) -> Option < ( ) > {
344- let result = unsafe { zend_hash_index_del ( self , key) } ;
460+ pub fn remove_index ( & mut self , key : i64 ) -> Option < ( ) > {
461+ let result = unsafe {
462+ #[ allow( clippy:: cast_sign_loss) ]
463+ zend_hash_index_del ( self , key as zend_ulong )
464+ } ;
345465
346466 if result < 0 {
347467 None
@@ -379,12 +499,54 @@ impl ZendHashTable {
379499 /// ht.insert("c", "C");
380500 /// assert_eq!(ht.len(), 3);
381501 /// ```
382- pub fn insert < V > ( & mut self , key : & str , val : V ) -> Result < ( ) >
502+ pub fn insert < ' a , K , V > ( & mut self , key : K , val : V ) -> Result < ( ) >
383503 where
504+ K : Into < ArrayKey < ' a > > ,
384505 V : IntoZval ,
385506 {
386507 let mut val = val. into_zval ( false ) ?;
387- unsafe { zend_hash_str_update ( self , CString :: new ( key) ?. as_ptr ( ) , key. len ( ) , & raw mut val) } ;
508+ match key. into ( ) {
509+ ArrayKey :: Long ( index) => {
510+ unsafe {
511+ #[ allow( clippy:: cast_sign_loss) ]
512+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
513+ } ;
514+ }
515+ ArrayKey :: String ( key) => {
516+ if let Ok ( index) = i64:: from_str ( & key) {
517+ unsafe {
518+ #[ allow( clippy:: cast_sign_loss) ]
519+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
520+ } ;
521+ } else {
522+ unsafe {
523+ zend_hash_str_update (
524+ self ,
525+ CString :: new ( key. as_str ( ) ) ?. as_ptr ( ) ,
526+ key. len ( ) ,
527+ & raw mut val,
528+ )
529+ } ;
530+ }
531+ }
532+ ArrayKey :: Str ( key) => {
533+ if let Ok ( index) = i64:: from_str ( key) {
534+ unsafe {
535+ #[ allow( clippy:: cast_sign_loss) ]
536+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
537+ } ;
538+ } else {
539+ unsafe {
540+ zend_hash_str_update (
541+ self ,
542+ CString :: new ( key) ?. as_ptr ( ) ,
543+ key. len ( ) ,
544+ & raw mut val,
545+ )
546+ } ;
547+ }
548+ }
549+ }
388550 val. release ( ) ;
389551 Ok ( ( ) )
390552 }
@@ -417,12 +579,15 @@ impl ZendHashTable {
417579 /// ht.insert_at_index(0, "C"); // notice overriding index 0
418580 /// assert_eq!(ht.len(), 2);
419581 /// ```
420- pub fn insert_at_index < V > ( & mut self , key : u64 , val : V ) -> Result < ( ) >
582+ pub fn insert_at_index < V > ( & mut self , key : i64 , val : V ) -> Result < ( ) >
421583 where
422584 V : IntoZval ,
423585 {
424586 let mut val = val. into_zval ( false ) ?;
425- unsafe { zend_hash_index_update ( self , key, & raw mut val) } ;
587+ unsafe {
588+ #[ allow( clippy:: cast_sign_loss) ]
589+ zend_hash_index_update ( self , key as zend_ulong , & raw mut val)
590+ } ;
426591 val. release ( ) ;
427592 Ok ( ( ) )
428593 }
@@ -560,6 +725,8 @@ impl ZendHashTable {
560725 /// }
561726 /// ArrayKey::String(key) => {
562727 /// }
728+ /// ArrayKey::Str(key) => {
729+ /// }
563730 /// }
564731 /// dbg!(key, val);
565732 /// }
@@ -612,15 +779,25 @@ pub struct Iter<'a> {
612779}
613780
614781/// Represents the key of a PHP array, which can be either a long or a string.
615- #[ derive( Debug , PartialEq ) ]
616- pub enum ArrayKey {
782+ #[ derive( Debug , Clone , PartialEq ) ]
783+ pub enum ArrayKey < ' a > {
617784 /// A numerical key.
785+ /// In Zend API it's represented by `u64` (`zend_ulong`), so the value needs
786+ /// to be cast to `zend_ulong` before passing into Zend functions.
618787 Long ( i64 ) ,
619788 /// A string key.
620789 String ( String ) ,
790+ /// A string key by reference.
791+ Str ( & ' a str ) ,
621792}
622793
623- impl ArrayKey {
794+ impl From < String > for ArrayKey < ' _ > {
795+ fn from ( value : String ) -> Self {
796+ Self :: String ( value)
797+ }
798+ }
799+
800+ impl ArrayKey < ' _ > {
624801 /// Check if the key is an integer.
625802 ///
626803 /// # Returns
@@ -630,21 +807,34 @@ impl ArrayKey {
630807 pub fn is_long ( & self ) -> bool {
631808 match self {
632809 ArrayKey :: Long ( _) => true ,
633- ArrayKey :: String ( _) => false ,
810+ ArrayKey :: String ( _) | ArrayKey :: Str ( _ ) => false ,
634811 }
635812 }
636813}
637814
638- impl Display for ArrayKey {
815+ impl Display for ArrayKey < ' _ > {
639816 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
640817 match self {
641818 ArrayKey :: Long ( key) => write ! ( f, "{key}" ) ,
642819 ArrayKey :: String ( key) => write ! ( f, "{key}" ) ,
820+ ArrayKey :: Str ( key) => write ! ( f, "{key}" ) ,
643821 }
644822 }
645823}
646824
647- impl < ' a > FromZval < ' a > for ArrayKey {
825+ impl < ' a > From < & ' a str > for ArrayKey < ' a > {
826+ fn from ( key : & ' a str ) -> ArrayKey < ' a > {
827+ ArrayKey :: Str ( key)
828+ }
829+ }
830+
831+ impl < ' a > From < i64 > for ArrayKey < ' a > {
832+ fn from ( index : i64 ) -> ArrayKey < ' a > {
833+ ArrayKey :: Long ( index)
834+ }
835+ }
836+
837+ impl < ' a > FromZval < ' a > for ArrayKey < ' _ > {
648838 const TYPE : DataType = DataType :: String ;
649839
650840 fn from_zval ( zval : & ' a Zval ) -> Option < Self > {
@@ -686,7 +876,7 @@ impl<'a> Iter<'a> {
686876}
687877
688878impl < ' a > IntoIterator for & ' a ZendHashTable {
689- type Item = ( ArrayKey , & ' a Zval ) ;
879+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
690880 type IntoIter = Iter < ' a > ;
691881
692882 /// Returns an iterator over the key(s) and value contained inside the
@@ -713,7 +903,7 @@ impl<'a> IntoIterator for &'a ZendHashTable {
713903}
714904
715905impl < ' a > Iterator for Iter < ' a > {
716- type Item = ( ArrayKey , & ' a Zval ) ;
906+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
717907
718908 fn next ( & mut self ) -> Option < Self :: Item > {
719909 self . next_zval ( )
@@ -1052,8 +1242,8 @@ impl FromIterator<Zval> for ZBox<ZendHashTable> {
10521242 }
10531243}
10541244
1055- impl FromIterator < ( u64 , Zval ) > for ZBox < ZendHashTable > {
1056- fn from_iter < T : IntoIterator < Item = ( u64 , Zval ) > > ( iter : T ) -> Self {
1245+ impl FromIterator < ( i64 , Zval ) > for ZBox < ZendHashTable > {
1246+ fn from_iter < T : IntoIterator < Item = ( i64 , Zval ) > > ( iter : T ) -> Self {
10571247 let mut ht = ZendHashTable :: new ( ) ;
10581248 for ( key, val) in iter {
10591249 // Inserting a zval cannot fail, as `push` only returns `Err` if converting
0 commit comments