Skip to content

PHPC-314: Implement type map syntax for documents within field paths #793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 18, 2018

Conversation

derickr
Copy link
Contributor

@derickr derickr commented Apr 4, 2018

First step of implementing the fieldPath syntax. It doesn't do wild cards yet, or typemaps for scalar types.

@derickr derickr requested review from jmikola and kvwalker April 4, 2018 15:42
@derickr derickr force-pushed the PHPC-314-typemap-for-fieldpaths branch 2 times, most recently from 625d3c4 to 67328be Compare April 13, 2018 12:10
parent::__construct($data);
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this was always dead code. Correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

var_dump(is_array($documents[0]->array));
var_dump($documents[0]->object instanceof MyArrayObject);
var_dump(is_array($documents[0]->object['string']));
var_dump(is_array($documents[0]->object->string));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this more of a test of ArrayObject::ARRAY_AS_PROPS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

$document2 = [
'_id' => 2,
'array' => [4, 5, 6],
'object' => ['associative' => ['elementen', 'elements' ]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this test is verifying that the Cursor's type map can be overridden and that the default behavior applies if a field path does not match. I think it'd be clearer to use names like "array1" and "array2" instead of "string" and "associative" for these BSON arrays, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was originally to see if we could set the typemap twice without leaking or memory issues.

But I added a functional bit to test that a bit better, and might ended up having just a test for PROPS_AS_ARRAY. Because of the memory issue/doubble setting, I would like to keep it though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure how ARRAY_AS_PROPS relates to the double setting of a type map, but no objections if you'd like to keep this in place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't — ARRAY_AS_PROPS was just added to make the asserts a little easier to write.

}

$documents = $cursor->toArray();
return $documents;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose these lines can be consolidated into return $cursor->toArray().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will fix that. I probably had a debug statement in between at some point.... which might still be useful in case an IDE is used though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow the relation to an IDE. If you're referring to an IDE indicating the type information for a $documents variable on these two lines, I don't think that's useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, if there is no variable, an IDE can't inspect its contents, as it can't see the direct return values of a function.

$documents = fetch($manager);
var_dump($documents[0] instanceof stdClass);
var_dump(is_array($documents[0]->array0));
var_dump(is_object($documents[0]->array1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems redundant in light of the instanceof assertion that follows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but I'm just being thorough.

src/bson.c Outdated
if (map->field_paths.allocated < map->field_paths.size + 1) {
map->field_paths.allocated += PHONGO_FIELD_PATH_EXPANSION;
map->field_paths.map = erealloc(map->field_paths.map, sizeof(php_phongo_field_path_map_element) * map->field_paths.allocated);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have an "ensure" function for this, or is this really the only place we need to increase allocation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the only place, and we always add to the end. Which is not the case when we build the current conversion field path for error messages and mapping, as there we leave nested sections as often as we go into them. I'll add a comment.

{
php_phongo_field_path_map_element* tmp = ecalloc(1, sizeof(php_phongo_field_path_map_element));

tmp->entry = php_phongo_field_path_alloc(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the element names should always be estrdup()-ed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/bson.c Outdated
return tmp;
}

bool php_phongo_bson_state_add_field_path(php_phongo_bson_typemap* map, char* field_path_original, php_phongo_bson_typemap_types type, zend_class_entry* ce TSRMLS_DC)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If field_path_map_element_set_info() doesn't need TSRMLS_DC, does this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't look like it, will remove it.

src/bson.c Outdated

/* Loop over all the segments. A segment is delimited by a "." */
element = php_strtok_r(field_path_str, ".", &saveptr);
while (element) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case where we don't enter the while loop on the first iteration?

It may be good to have error tests for field paths such as "...", ".foo", and "" so we can test how that is handled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we won't ever enter, but I will add test cases. I know that strtok handles ... as . for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh, strtok_r is pretty useless, as it can't handle empty elements (like in foo..bar), so I've had to write this, which also means that we needed to keep some TSRMLS_[DC]C things around.

return false;
}

if (!php_phongo_bson_state_add_field_path(map, ZSTR_VAL(string_key), map_type, map_ce TSRMLS_CC)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

php_phongo_bson_state_add_field_path() always returns true. In the event a failure case is introduced, I suppose we'll want to ensure it throws an exception. Correct?

Is that why you're preemptively passing in TSRMLS_CC here (consistency with php_phongo_bson_state_parse_type())?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd wish to say that I added TSRMLS_CC because of that, but that's not true :-) But yes, perhaps we should leave it as long as the strtok loop can create failure notifications with the three cases that you suggested.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye, let's revisit this if you decide to add exceptions for tokenization errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which I've now just done :-).

Copy link
Contributor Author

@derickr derickr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR with changes related to your comments - except for the ones that you didn't comment on after my first reply.

src/bson.c Outdated
field_path->elements = erealloc(field_path->elements, sizeof(char**) * field_path->allocated_levels);
field_path->allocated = field_path->size + PHONGO_FIELD_PATH_EXPANSION;
field_path->elements = erealloc(field_path->elements, sizeof(char**) * field_path->allocated);
field_path->element_types = erealloc(field_path->element_types, sizeof(php_phongo_bson_field_path_item_types*) * field_path->allocated);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Narrator: It did]

src/bson.c Outdated

/* Loop over all the segments. A segment is delimited by a "." */
element = php_strtok_r(field_path_str, ".", &saveptr);
while (element) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh, strtok_r is pretty useless, as it can't handle empty elements (like in foo..bar), so I've had to write this, which also means that we needed to keep some TSRMLS_[DC]C things around.

return false;
}

if (!php_phongo_bson_state_add_field_path(map, ZSTR_VAL(string_key), map_type, map_ce TSRMLS_CC)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which I've now just done :-).

$document2 = [
'_id' => 2,
'array' => [4, 5, 6],
'object' => ['associative' => ['elementen', 'elements' ]]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't — ARRAY_AS_PROPS was just added to make the asserts a little easier to write.

}

$documents = $cursor->toArray();
return $documents;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, if there is no variable, an IDE can't inspect its contents, as it can't see the direct return values of a function.

var_dump($documents[0] instanceof stdClass);
var_dump(is_array($documents[0]->array));
var_dump($documents[0]->array[0] instanceof MyWildCardArrayObject);
var_dump($documents[0]->array[1] instanceof MyArrayObject);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah!::

class MyWildcardArrayObject extends MyArrayObject {};

So of course it matches :-) I will get rid of the extends, and repeat the MyArrayObject implementation.

Luckily the implementation was fine, just not the test.

var_dump($documents[0] instanceof stdClass);
var_dump(is_object($documents[0]->object));
var_dump($documents[0]->object->one instanceof MyArrayObject);
var_dump($documents[0]->object->two instanceof MyWildCardArrayObject);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

}
}

class MyWildCardArrayObject extends MyArrayObject {};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Narrator: They were changed to MyWildcardArrayObject anyway]

echo "\nSetting 'object.parent1.$' path to 'MyWildCardArrayObject' and 'object.$.$' to 'MyArrayObject'\n";
$documents = fetch($manager, ["fieldPaths" => [
'object.parent1.$' => "MyWildCardArrayObject",
'object.$.$' => "MyArrayObject",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, added this.

$cursor->setTypeMap($typeMap);
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";

echo "\n";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@derickr derickr force-pushed the PHPC-314-typemap-for-fieldpaths branch 2 times, most recently from 500db06 to 7fba7f5 Compare April 17, 2018 12:21

if (field_path_original[strlen(field_path_original) - 1] == '.') {
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "A 'fieldPaths' key may not end with a '.'");
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming both of these cases are handled by the "empty segment" error below, I'd propose allowing that to catch this. IMO, ".foo", "foo..bar", and "foo." all contain empty segments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that first, but thought it better we have more precise error messages. This one is also faster to check as nothing has been allocated yet.

@@ -0,0 +1,67 @@
--TEST--
Cursor::setTypeMap(): fieldPaths must be an array, with single key/string elements
Copy link
Member

@jmikola jmikola Apr 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and it tests the same thing as the error-001 and error-002 tests, so it should be the same here — and not as toPHP_error

Not sure I follow. My suggestion was that once we finalize these tests for Cursor::setTypeMap() we copy them for toPHP() to ensure that function passes as well.

Separate from this, can we have all of the current tests organized under cursor/cursor-setTypeMap*? Any other tests that get copied can then go under bson/bson-toPHP*

EDIT: Just saw your reply in #793 (comment) about wanting to keep things under bson/typemap*. Point noted, but will we have tests for toPHP() or do you think that's superfluous?

--TEST--
MongoDB\BSON\toPHP(): fieldPath typemaps without server
--SKIPIF--
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
Copy link
Member

@jmikola jmikola Apr 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server-less tests shouldn't import this, as it ultimately requires PHONGO-SERVERS.json to exist. The extension check in basic-skipif.inc isn't very helpful anyway.

I suggest just omitting SKIPIF here, as is done in other server-less tests.

@derickr derickr force-pushed the PHPC-314-typemap-for-fieldpaths branch from 99394d7 to d0e8318 Compare April 17, 2018 14:05
@derickr derickr force-pushed the PHPC-314-typemap-for-fieldpaths branch from d0e8318 to da8853a Compare April 18, 2018 10:04
@derickr derickr merged commit da8853a into mongodb:master Apr 18, 2018
derickr added a commit that referenced this pull request Apr 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants