Skip to content

Commit e6f6700

Browse files
committed
feat(array): has_key()
1 parent 4918ad8 commit e6f6700

File tree

1 file changed

+89
-1
lines changed

1 file changed

+89
-1
lines changed

src/types/array/mod.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,12 +693,56 @@ impl ZendHashTable {
693693
K: Into<ArrayKey<'k>>,
694694
{
695695
let key = key.into();
696-
if self.get(key.clone()).is_some() {
696+
if self.has_key(&key) {
697697
Entry::Occupied(entry::OccupiedEntry::new(self, key))
698698
} else {
699699
Entry::Vacant(entry::VacantEntry::new(self, key))
700700
}
701701
}
702+
703+
/// Checks if a key exists in the hash table.
704+
///
705+
/// # Parameters
706+
///
707+
/// * `key` - The key to check for in the hash table.
708+
///
709+
/// # Returns
710+
///
711+
/// * `true` - The key exists in the hash table.
712+
/// * `false` - The key does not exist in the hash table.
713+
///
714+
/// # Example
715+
///
716+
/// ```no_run
717+
/// use ext_php_rs::types::{ZendHashTable, ArrayKey};
718+
///
719+
/// let mut ht = ZendHashTable::new();
720+
///
721+
/// ht.insert("test", "hello world");
722+
/// assert!(ht.has_key(&ArrayKey::from("test")));
723+
/// assert!(!ht.has_key(&ArrayKey::from("missing")));
724+
/// ```
725+
#[must_use]
726+
pub fn has_key(&self, key: &ArrayKey<'_>) -> bool {
727+
match key {
728+
ArrayKey::Long(index) => unsafe {
729+
#[allow(clippy::cast_sign_loss)]
730+
!zend_hash_index_find(self, *index as zend_ulong).is_null()
731+
},
732+
ArrayKey::String(key) => {
733+
let Ok(cstr) = CString::new(key.as_str()) else {
734+
return false;
735+
};
736+
unsafe { !zend_hash_str_find(self, cstr.as_ptr(), key.len() as _).is_null() }
737+
}
738+
ArrayKey::Str(key) => {
739+
let Ok(cstr) = CString::new(*key) else {
740+
return false;
741+
};
742+
unsafe { !zend_hash_str_find(self, cstr.as_ptr(), key.len() as _).is_null() }
743+
}
744+
}
745+
}
702746
}
703747

704748
unsafe impl ZBoxable for ZendHashTable {
@@ -762,3 +806,47 @@ impl<'a> FromZval<'a> for &'a ZendHashTable {
762806
zval.array()
763807
}
764808
}
809+
810+
#[cfg(test)]
811+
#[cfg(feature = "embed")]
812+
mod tests {
813+
use super::*;
814+
use crate::embed::Embed;
815+
816+
#[test]
817+
fn test_has_key_string() {
818+
Embed::run(|| {
819+
let mut ht = ZendHashTable::new();
820+
let _ = ht.insert("test", "value");
821+
822+
assert!(ht.has_key(&ArrayKey::from("test")));
823+
assert!(!ht.has_key(&ArrayKey::from("missing")));
824+
});
825+
}
826+
827+
#[test]
828+
fn test_has_key_long() {
829+
Embed::run(|| {
830+
let mut ht = ZendHashTable::new();
831+
let _ = ht.push(42i64);
832+
833+
assert!(ht.has_key(&ArrayKey::Long(0)));
834+
assert!(!ht.has_key(&ArrayKey::Long(1)));
835+
});
836+
}
837+
838+
#[test]
839+
fn test_has_key_str_ref() {
840+
Embed::run(|| {
841+
let mut ht = ZendHashTable::new();
842+
let _ = ht.insert("hello", "world");
843+
844+
let key = ArrayKey::Str("hello");
845+
assert!(ht.has_key(&key));
846+
// Key is still usable after has_key (no clone needed)
847+
assert!(ht.has_key(&key));
848+
849+
assert!(!ht.has_key(&ArrayKey::Str("missing")));
850+
});
851+
}
852+
}

0 commit comments

Comments
 (0)