Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Stdlib\Options: Misc enhancements #6884

Conversation

Pittiplatsch
Copy link

  • Minor performance tweak - consolidate calls to method_exists
  • Replace method_exists with more reliable is_callable
  • CS fixes

* Replace method_exists with more reliable is_callable
* CS fixes
@Martin-P
Copy link
Contributor

Replace method_exists with more reliable is_callable

Behaviour is different, is_callable() also returns true for magic methods. This can be considered a BC break. Furthermore, is_callable() is slightly slower than method_exists().

throw new Exception\BadMethodCallException(
'The option "' . $key . '" does not '
. 'have a callable "' . $setter . '" setter method '
. 'which must be defined'
Copy link
Contributor

Choose a reason for hiding this comment

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

ZF2 normally uses sprintf() for exception messages.

throw new Exception\BadMethodCallException(sprintf(
    'The option "%s" does not  have a callable '
    . '"%s" setter method which must be defined',
    $key,
    $setter
));

@Pittiplatsch
Copy link
Author

is_callable returning true for magic methods isn't relevant here due to predefined method prefixes get / set which obviously don't touch magic methods.
However, is_callable respects method visibility / accessibility, where method_exist doesn't. Moreover, is_callable has improved testability when it comes to mocked methods, while mocked methods don't play well with method_exists.

@Martin-P
Copy link
Contributor

When someone uses __call() in their class, this change is a BC break:

<?php

namespace MyNamespace;

use Zend\Stdlib\AbstractOptions;

class CustomOptions extends AbstractOptions
{
    public function __call($methodName, $arguments)
    {
    }
}

$options = new CustomOptions();
var_dump(is_callable(array($options, 'getFoo'))); // true
var_dump(method_exists($options, 'getFoo'));      // false

@Pittiplatsch
Copy link
Author

Uh, you're right - didn't see that :-(

Don't know though if this (undoubtly existing) BC is relevant.
It could also be treated as new feature "allow getters / setters implemented via magic __call". I can hardly think of a use-case where this BC applies, as it doesn't prevent any existing functionality, but allows new implementation.

);
} elseif (!$this->__strictMode__ && !method_exists($this, $setter)) {
return;
if (!is_callable(array($this, $setter))) {
Copy link
Member

Choose a reason for hiding this comment

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

You can swap this conditional by making the setter call if is_callable(), and handling exceptions later on. Simplifies the method by a lot.

Copy link
Member

Choose a reason for hiding this comment

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

Wondering if a static local cache would bring any benefit here, or if it's a total overkill.

@Ocramius Ocramius added this to the 2.3.4 milestone Nov 22, 2014
@Ocramius Ocramius self-assigned this Nov 22, 2014
@Martin-P
Copy link
Contributor

@Ocramius I think the milestone should be 2.4.0 because is_callable() introduces a BC break. See my previous comment: #6884 (comment)

@Ocramius
Copy link
Member

When someone uses __call() in their class, this change is a BC break

Requires a documentation change: using __call() on this kind of class (in a subclass) is at the limit of nonsense IMO :-)

And yes, moved to 2.4

@Ocramius Ocramius modified the milestones: 2.4.0, 2.3.4 Nov 22, 2014
@Ocramius
Copy link
Member

@Pittiplatsch can you check the new behavior with a test that throws an exception with protected and private methods?

@Pittiplatsch
Copy link
Author

Will think about it tomorrow.

@Pittiplatsch
Copy link
Author

Tests added, but one of them fails although IMO it should succeed.
This however looks like a general issue about visibility I never realized.
See this gist for details: https://gist.github.com/Pittiplatsch/f0ca0485d8e96ca91318

@Ocramius
Copy link
Member

Played with your gist a bit: http://3v4l.org/mQ57X

May be related with https://bugs.php.net/bug.php?id=29210 and https://bugs.php.net/bug.php?id=33277, not sure tho.

It seems to me that is_callable() considers the current scope when detecting if a method is callable.

This makes sense though, as it is indeed not possible to call private methods from the scope of parent or child classes.

@Pittiplatsch
Copy link
Author

IMO is_callable() returns correct results in whether a method can / may actually be called at the position of testing it.
However, I am struggling with the fact that methods being defined in child class cannot be called from a parent's method, although the class instance (and therefore as I thought the scope?!?) exactly is the child which obviously IS allowed to access its own private method.
As I already stated in the beginning of my gist, with the given situation there is no way of implementing IOC with abstract private methods, right?

@Ocramius
Copy link
Member

As I already stated in the beginning of my gist, with the given situation there is no way of implementing IOC with abstract private methods, right?

Correct. private members are meant for implementation details.

@Pittiplatsch
Copy link
Author

"Again what learned" ;-)
Ergo: Those unit tests which fail do so justifiably => I'll adjust them accordingly, ok?

@Ocramius
Copy link
Member

@Pittiplatsch right: I think we have to leave the BC Break label though.

@Pittiplatsch
Copy link
Author

Unit tests are passing now.

BC break: IMO this isn't necessary, because switching from method_exists to is_callable() is:

@Ocramius
Copy link
Member

@Pittiplatsch it still needs documentation for these edge-cases (documented limitation)

Ocramius added a commit that referenced this pull request Dec 6, 2014
Ocramius added a commit that referenced this pull request Dec 6, 2014
Ocramius added a commit that referenced this pull request Dec 6, 2014
Ocramius added a commit that referenced this pull request Dec 6, 2014
@Ocramius
Copy link
Member

Ocramius commented Dec 6, 2014

@Pittiplatsch merged into develop at 729e4be, thanks!

@Ocramius Ocramius closed this Dec 6, 2014
@Pittiplatsch Pittiplatsch deleted the stdlib_options_performance branch January 15, 2015 12:38
gianarb pushed a commit to zendframework/zend-stdlib that referenced this pull request May 15, 2015
gianarb pushed a commit to zendframework/zend-stdlib that referenced this pull request May 15, 2015
gianarb pushed a commit to zendframework/zend-stdlib that referenced this pull request May 15, 2015
…ng exception expectations at the correct point in code execution
gianarb pushed a commit to zendframework/zend-stdlib that referenced this pull request May 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants