Skip to content

Commit 95806e0

Browse files
Merge pull request #3 from ChessCom/php-isset-fix
[php][ext] - Fix isset behaviour
2 parents 5c30570 + e7da2a1 commit 95806e0

File tree

3 files changed

+53
-14
lines changed

3 files changed

+53
-14
lines changed

php/ext/google/protobuf/message.c

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ static void Message_dtor(zend_object* obj) {
8585
}
8686

8787
/**
88-
* get_field()
88+
* lookup_field()
8989
*
9090
* Helper function to look up a field given a member name (as a string).
9191
*/
92-
static const upb_FieldDef* get_field(Message* msg, zend_string* member) {
92+
static const upb_FieldDef* lookup_field(Message* msg, zend_string* member) {
9393
if (!msg || !msg->desc || !msg->desc->msgdef) {
9494
zend_throw_exception_ex(NULL, 0,
9595
"Couldn't find descriptor. "
@@ -104,8 +104,26 @@ static const upb_FieldDef* get_field(Message* msg, zend_string* member) {
104104
m, ZSTR_VAL(member), ZSTR_LEN(member));
105105

106106
if (!f) {
107-
zend_throw_exception_ex(NULL, 0, "No such property %s.",
108-
ZSTR_VAL(msg->desc->class_entry->name));
107+
return NULL;
108+
}
109+
110+
return f;
111+
}
112+
113+
/**
114+
* get_field()
115+
*
116+
* Helper function to get up a field given a member name (as a string).
117+
* if the field is not found, a PHP warning is emitted and NULL is returned.
118+
*/
119+
static const upb_FieldDef* get_field(Message* msg, zend_string* member) {
120+
const upb_FieldDef* f = lookup_field(msg, member);
121+
122+
if (!f && msg && msg->desc) {
123+
// if the descriptor is undefined, an exception was already thrown.
124+
php_error(E_WARNING, "Undefined property: %s::$%s",
125+
ZSTR_VAL(msg->desc->class_entry->name),
126+
ZSTR_VAL(member));
109127
}
110128

111129
return f;
@@ -250,15 +268,11 @@ static int Message_compare_objects(zval* m1, zval* m2) {
250268
static int Message_has_property(zend_object* obj, zend_string* member,
251269
int has_set_exists, void** cache_slot) {
252270
Message* intern = (Message*)obj;
253-
const upb_FieldDef* f = get_field(intern, member);
271+
const upb_FieldDef* f = lookup_field(intern, member);
254272

255273
if (!f) return 0;
256274

257-
if (!upb_FieldDef_HasPresence(f)) {
258-
zend_throw_exception_ex(
259-
NULL, 0,
260-
"Cannot call isset() on field %s which does not have presence.",
261-
upb_FieldDef_Name(f));
275+
if (upb_FieldDef_IsOptional(f) && !upb_FieldDef_HasPresence(f)) {
262276
return 0;
263277
}
264278

@@ -284,7 +298,7 @@ static int Message_has_property(zend_object* obj, zend_string* member,
284298
static void Message_unset_property(zend_object* obj, zend_string* member,
285299
void** cache_slot) {
286300
Message* intern = (Message*)obj;
287-
const upb_FieldDef* f = get_field(intern, member);
301+
const upb_FieldDef* f = lookup_field(intern, member);
288302

289303
if (!f) return;
290304

php/tests/GeneratedClassTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,31 @@ public function testSetterGetter()
5656
$this->assertSame(1, $m->getOptionalInt32());
5757
}
5858

59+
#########################################################
60+
# Test isset / property_exists
61+
#########################################################
62+
63+
public function testIsset()
64+
{
65+
$message = new TestMessage();
66+
67+
$this->assertTrue(property_exists(TestMessage::class, 'optional_string'));
68+
$this->assertTrue(property_exists($message, 'optional_string'));
69+
$this->assertFalse(isset($message->optional_string));
70+
71+
$message->setOptionalString('bar');
72+
73+
$this->assertTrue(property_exists(TestMessage::class, 'optional_string'));
74+
$this->assertFalse(property_exists(TestMessage::class, 'foo'));
75+
76+
$this->assertTrue(property_exists($message, 'optional_string'));
77+
$this->assertFalse(property_exists($message, 'foo'));
78+
79+
// isset() should return false for attributes that are not public.
80+
$this->assertFalse(isset($message->optional_string));
81+
$this->assertFalse(isset($message->foo));
82+
}
83+
5984
#########################################################
6085
# Test int32 field.
6186
#########################################################

src/google/protobuf/repeated_ptr_field_unittest.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ TEST(RepeatedPtrFieldTest, AddAllocated) {
559559
EXPECT_EQ(moo, &field.Get(index));
560560
}
561561

562-
TEST(RepeatedPtrFieldTest, AddMethodsDontAcceptNull) {
562+
TEST(RepeatedPtrFieldDeathTest, AddMethodsDontAcceptNull) {
563563
#if !defined(NDEBUG)
564564
RepeatedPtrField<std::string> field;
565565
EXPECT_DEATH(field.AddAllocated(nullptr), "nullptr");
@@ -1112,7 +1112,7 @@ TEST(RepeatedPtrFieldTest, Cleanups) {
11121112
}
11131113

11141114

1115-
TEST(RepeatedPtrFieldTest, CheckedGetOrAbortTest) {
1115+
TEST(RepeatedPtrFieldDeathTest, CheckedGetOrAbortTest) {
11161116
RepeatedPtrField<std::string> field;
11171117

11181118
// Empty container tests.
@@ -1127,7 +1127,7 @@ TEST(RepeatedPtrFieldTest, CheckedGetOrAbortTest) {
11271127
EXPECT_DEATH(internal::CheckedGetOrAbort(field, -1), "index: -1, size: 2");
11281128
}
11291129

1130-
TEST(RepeatedPtrFieldTest, CheckedMutableOrAbortTest) {
1130+
TEST(RepeatedPtrFieldDeathTest, CheckedMutableOrAbortTest) {
11311131
RepeatedPtrField<std::string> field;
11321132

11331133
// Empty container tests.

0 commit comments

Comments
 (0)