Skip to content

Commit 461ef5b

Browse files
committed
fix(property): add private property check
Refs: #375
1 parent 30fd19f commit 461ef5b

File tree

5 files changed

+69
-12
lines changed

5 files changed

+69
-12
lines changed

allowed_bindings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ bind! {
9494
zend_hash_str_del,
9595
zend_hash_str_find,
9696
zend_hash_str_update,
97+
zend_hash_update_ind,
9798
zend_internal_arg_info,
9899
zend_is_callable,
99100
zend_is_identical,
@@ -116,6 +117,7 @@ bind! {
116117
zend_resource,
117118
zend_string,
118119
zend_string_init_interned,
120+
zend_throw_error,
119121
zend_throw_exception_ex,
120122
zend_throw_exception_object,
121123
zend_type,

src/zend/handlers.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
use std::{ffi::c_void, mem::MaybeUninit, os::raw::c_int, ptr};
1+
use std::{
2+
ffi::{c_void, CString},
3+
mem::MaybeUninit,
4+
os::raw::c_int,
5+
ptr,
6+
};
27

38
use crate::{
49
class::RegisteredClass,
510
exception::PhpResult,
611
ffi::{
7-
std_object_handlers, zend_is_true, zend_object_handlers, zend_object_std_dtor,
8-
zend_std_get_properties, zend_std_has_property, zend_std_read_property,
9-
zend_std_write_property,
12+
_zend_class_entry, std_object_handlers, zend_hash_update_ind, zend_is_true,
13+
zend_object_handlers, zend_object_std_dtor, zend_std_get_properties, zend_std_has_property,
14+
zend_std_read_property, zend_std_write_property, zend_throw_error,
1015
},
11-
flags::ZvalTypeFlags,
16+
flags::{PropertyFlags, ZvalTypeFlags},
1217
types::{ZendClassObject, ZendHashTable, ZendObject, ZendStr, Zval},
1318
};
1419

@@ -92,6 +97,7 @@ impl ZendObjectHandlers {
9297
let prop_name = member
9398
.as_ref()
9499
.ok_or("Invalid property name pointer given")?;
100+
95101
let self_ = &mut **obj;
96102
let props = T::get_metadata().get_properties();
97103
let prop = props.get(prop_name.as_str()?);
@@ -102,6 +108,9 @@ impl ZendObjectHandlers {
102108

103109
Ok(match prop {
104110
Some(prop_info) => {
111+
if prop_info.flags.contains(PropertyFlags::Private) {
112+
bad_property_access::<T>(prop_name.as_str()?);
113+
}
105114
prop_info.prop.get(self_, rv_mut)?;
106115
rv
107116
}
@@ -148,6 +157,9 @@ impl ZendObjectHandlers {
148157

149158
Ok(match prop {
150159
Some(prop_info) => {
160+
if prop_info.flags.contains(PropertyFlags::Private) {
161+
bad_property_access::<T>(prop_name.as_str()?);
162+
}
151163
prop_info.prop.set(self_, value_mut)?;
152164
value
153165
}
@@ -186,9 +198,19 @@ impl ZendObjectHandlers {
186198
if val.prop.get(self_, &mut zv).is_err() {
187199
continue;
188200
}
189-
props.insert(name, zv).map_err(|e| {
190-
format!("Failed to insert value into properties hashtable: {e:?}")
191-
})?;
201+
202+
let name = if val.flags.contains(PropertyFlags::Private) {
203+
println!("Private property {name}");
204+
format!("\0{}\0{name}", T::CLASS_NAME)
205+
} else if val.flags.contains(PropertyFlags::Protected) {
206+
println!("Protected property {name}");
207+
format!("\0*\0{name}")
208+
} else {
209+
(*name).to_string()
210+
};
211+
let mut name = ZendStr::new(name, false);
212+
213+
zend_hash_update_ind(props, name.as_mut_ptr(), &mut zv);
192214
}
193215

194216
Ok(())
@@ -296,3 +318,22 @@ impl ZendObjectHandlers {
296318
}
297319
}
298320
}
321+
322+
/// Throws the error if a property is accessed incorrectly.
323+
/// This only throws the error and does not perform any checks.
324+
///
325+
/// # Panics
326+
///
327+
/// * If the property name is not a valid c string.
328+
unsafe fn bad_property_access<T: RegisteredClass>(prop_name: &str) {
329+
zend_throw_error(
330+
ptr::null_mut::<_zend_class_entry>(),
331+
CString::new(format!(
332+
"Cannot access private property {}::${}",
333+
T::CLASS_NAME,
334+
prop_name
335+
))
336+
.expect("Failed to convert property name")
337+
.as_ptr(),
338+
);
339+
}

tests/src/integration/class/class.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<?php
22

3+
declare(strict_types=1);
4+
5+
require(__DIR__ . '/../_utils.php');
6+
37
// Tests constructor
48
$class = test_class('lorem ipsum', 2022);
5-
assert($class instanceof TestClass);
9+
assert($class instanceof Foo\TestClass);
610

711
// Tests getter/setter
812
assert($class->getString() === 'lorem ipsum');
@@ -25,4 +29,7 @@
2529
assert($class::staticCall('Php') === 'Hello Php');
2630

2731
// Call static from class
28-
assert(TestClass::staticCall('Php') === 'Hello Php');
32+
assert(Foo\TestClass::staticCall('Php') === 'Hello Php');
33+
34+
assert_exception_thrown(fn() => $class->private_string = 'private2');
35+
assert_exception_thrown(fn() => $class->private_string);

tests/src/integration/class/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use ext_php_rs::prelude::*;
22

33
#[php_class]
4+
#[php(name = "Foo\\TestClass")]
45
pub struct TestClass {
56
string: String,
67
number: i32,
78
#[php(prop)]
89
boolean: bool,
10+
#[php(prop, flags = "ext_php_rs::flags::PropertyFlags::Private")]
11+
private_string: String,
12+
#[php(prop, flags = ext_php_rs::flags::PropertyFlags::Protected)]
13+
protected_string: String,
914
}
1015

1116
#[php_impl]
@@ -41,6 +46,8 @@ pub fn test_class(string: String, number: i32) -> TestClass {
4146
string,
4247
number,
4348
boolean: true,
49+
private_string: "private".to_string(),
50+
protected_string: "protected".to_string(),
4451
}
4552
}
4653

tests/src/integration/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ mod test {
6464
stderr: {}
6565
",
6666
output.status,
67-
String::from_utf8(output.stdout).unwrap(),
68-
String::from_utf8(output.stderr).unwrap()
67+
String::from_utf8_lossy(&output.stdout),
68+
String::from_utf8_lossy(&output.stderr)
6969
);
7070
}
7171
}

0 commit comments

Comments
 (0)