Skip to content
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

Add function where() #135

Closed
wants to merge 2 commits into from
Closed

Add function where() #135

wants to merge 2 commits into from

Conversation

Erikvv
Copy link
Contributor

@Erikvv Erikvv commented Apr 4, 2017

Resubmit of #122 with some feedback processed

@Erikvv Erikvv mentioned this pull request Apr 4, 2017
@lstrojny
Copy link
Owner

lstrojny commented Apr 4, 2017

I am contemplating the priority of properties over methods right now. From a puristic perspective I think it’s the right thing, from a "this is how PHP code looks nowadays mostly" I feel we should test for methods first, than for properties. WDYT?

* @param array $propertyMap
* @return bool
*/
$matches = function($object, array $propertyMap) use ($readProperty, &$matches) {
Copy link
Owner

Choose a reason for hiding this comment

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

Any reason this should be a instead of just code inside of the foreach loop?

Copy link
Contributor Author

@Erikvv Erikvv Apr 4, 2017

Choose a reason for hiding this comment

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

If I inline it, the body of where() would look like this:

    InvalidArgumentException::assertCollection($objects, __FUNCTION__, 1);

    $result = [];
    foreach ($objects as $obj)
    {
        foreach ($propertyMap as $property => $value) {
            if (is_array($value)) {
                if (!$matches($readProperty($object, $property), $value)) {
                    continue 2;
                } else {
                    continue;
                }
            }

            try {
                // weak equality check to compare different instances of objects
                if ($readProperty($object, $property) != $value) {
                    continue 2;
                }
            } catch (\OutOfBoundsException $outOfBoundsException) {
                continue 2;
            }
        }

        $result[] = $obj;
    }
    return $result;

I think it is unclear what is happening in this code. If one of my colleages commited this it would raise an eyebrow.

Copy link
Owner

Choose a reason for hiding this comment

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

I agree, not pretty but for a library like this where we try add as little overhead as possible, every function call counts

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'll run a benchmark to see what the performance difference is. The function call may not have that much of an inpact due to the pointer chaising and reflection that we have regardless.

Copy link
Owner

Choose a reason for hiding this comment

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

That makes sense!

Copy link
Contributor Author

@Erikvv Erikvv Apr 4, 2017

Choose a reason for hiding this comment

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

Unfortunately it indeed makes a pretty big difference, around 50% slowdown in the worst-case.

Copy link
Owner

Choose a reason for hiding this comment

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

Doing the function call is worse or inlining the code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doing the function call is 50% slower in the worst case, meaning when the shortest path is taken through the code so function call overhead is maximized

Copy link
Owner

Choose a reason for hiding this comment

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

Yep, that was my intuition but good that we verified it

@Erikvv
Copy link
Contributor Author

Erikvv commented Apr 4, 2017

W.r.t. priority of properties and methods I think you have a point that methods should take precedence. It would prevent some access violations.

There are 2 real-world situations that I can think of where this occurs.

  1. Boolean properties like $isValid which may be cached behing a getter ->isValid()
  2. (Immutable) objects where someones choses not to prefix the getter with "get".

I'll switch it around to methods take precedence.

@armetiz
Copy link

armetiz commented Mar 1, 2018

Any news?

throw new \OutOfBoundsException;
}
} else {
throw new \InvalidArgumentException('Expected object or array');
Copy link
Owner

Choose a reason for hiding this comment

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

"Expected …, got …" would help a lot while debugging

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed

if (array_key_exists($property, $object)) {
return $object[$property];
} else {
throw new \OutOfBoundsException;
Copy link
Owner

Choose a reason for hiding this comment

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

Proper error message missing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed

if (is_object($object)) {
if (method_exists($object, $property)) {
return $object->{$property}();
} elseif (property_exists($object, $property)) {
Copy link
Owner

Choose a reason for hiding this comment

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

property_exists() will return true for private properties. in_array() + get_class_vars() should work 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.

Isn't that a usefull error though "can't access private property"?

Copy link
Owner

Choose a reason for hiding this comment

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

Principle of the least surprise for me would mean for it to behave like a a non-existent property. Imagine you are using where() on a list and you catch the InvalidArgumentException because you foresee some property being missing. Now you go ahead and change the class that is used somewhere in you application and introduce a private property and suddenly you have a different error case. I would handle it consistently with the "non-existent property case".

@Erikvv
Copy link
Contributor Author

Erikvv commented Mar 2, 2018

I don't want to do more inlining. That's why I've let go of the PR.

if (is_object($object)) {
if (method_exists($object, $property)) {
return $object->{$property}();
} elseif (property_exists($object, $property)) {
Copy link
Owner

Choose a reason for hiding this comment

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

Principle of the least surprise for me would mean for it to behave like a a non-existent property. Imagine you are using where() on a list and you catch the InvalidArgumentException because you foresee some property being missing. Now you go ahead and change the class that is used somewhere in you application and introduce a private property and suddenly you have a different error case. I would handle it consistently with the "non-existent property case".

Multiple properties can be matched for. They must all match.

```php
$fredsFromGithub = where($users, [
Copy link
Owner

Choose a reason for hiding this comment

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

Being German this must totally $fredFromJupiter

```php
$fredsFromGithub = where($users, [
'name' => 'Fred',
'organization' => 'GitHub'
Copy link
Owner

Choose a reason for hiding this comment

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

… and Jupiter here 😄

@lstrojny
Copy link
Owner

lstrojny commented Dec 3, 2018

Closing for inactivity

@lstrojny lstrojny closed this Dec 3, 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