Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL).
I tried to mock this interface as you would mock anything else:
use Ddeboer\Imap\MailboxInterface;
\Mockery::mock(MailboxInterface::class)
However, this generated the following error message:
Declaration of Mockery_2_IteratorAggregate_Ddeboer_Imap_MailboxInterface::getIterator() must be compatible with Ddeboer\Imap\MailboxInterface::getIterator(): Ddeboer\Imap\MessageIteratorInterface
I did a lot of digging into the internals of Mockery to figure out what was happening, but I think I found the culprit.
The MailboxInterface class extends from both Countable and IteratorAggregate. But it extends the definition of IteratorAggregate::getIterator with a return type of MessageIteratorInterface. Mockery however seems to try to mock the method from IteratorAggregate, which of course doesn't match the one from MailboxInterface.
This is evidence by tracing the allMethods array in MockConfiguration, which as the very first entry contains the ReflectionMethod pointing to IteratorAggregate::getIterator. And this is (probably, I didn't check this) caused by IteratorAggregate being added to the targetInterfaces array.
This doesn't seem like the way it should work, and I think you'll agree with me, because this line would seem to be intended to filter out IteratorAggregate from the interface list:
But this looks all OK, right? preg_match("/^\\?Iterator(|Aggregate)$/i", "IteratorAggregate") should match, right? Online regex testers like regex101.com all say this matches, so it does, doesn't it?
Except that it doesn't. That function returns 0. I tested various PHP versions (5.6, 7.0, 7.4, 8.0) and operating systems (Debian, Ubuntu, Alpine, CentOS) and they all return 0. Please do run php -r 'echo preg_match("/^\\?Iterator(|Aggregate)$/i", "IteratorAggregate");' on your platform of choice too to check it!
I'm utterly confused by what is happening here. Maybe I overlooked something important. But for some reason there is code in place to filter out IteratorAggregate and it doesn't work due to some very strange behavior in PCRE.
Earlier today, I tried to write a test involving where I wanted to mock the MailboxInterface class from DDeBoer's IMAP library: https://github.com/ddeboer/imap/blob/master/src/MailboxInterface.php
I tried to mock this interface as you would mock anything else:
However, this generated the following error message:
I did a lot of digging into the internals of Mockery to figure out what was happening, but I think I found the culprit.
The
MailboxInterface
class extends from bothCountable
andIteratorAggregate
. But it extends the definition ofIteratorAggregate::getIterator
with a return type ofMessageIteratorInterface
. Mockery however seems to try to mock the method fromIteratorAggregate
, which of course doesn't match the one fromMailboxInterface
.This is evidence by tracing the
allMethods
array inMockConfiguration
, which as the very first entry contains theReflectionMethod
pointing toIteratorAggregate::getIterator
. And this is (probably, I didn't check this) caused byIteratorAggregate
being added to thetargetInterfaces
array.This doesn't seem like the way it should work, and I think you'll agree with me, because this line would seem to be intended to filter out IteratorAggregate from the interface list:
https://github.com/mockery/mockery/blob/d1339f64479af1bee0e82a0413813fe5345a54ea/library/Mockery/Generator/MockConfiguration.php#L382
But this looks all OK, right?
preg_match("/^\\?Iterator(|Aggregate)$/i", "IteratorAggregate")
should match, right? Online regex testers like regex101.com all say this matches, so it does, doesn't it?Except that it doesn't. That function returns
0
. I tested various PHP versions (5.6, 7.0, 7.4, 8.0) and operating systems (Debian, Ubuntu, Alpine, CentOS) and they all return 0. Please do runphp -r 'echo preg_match("/^\\?Iterator(|Aggregate)$/i", "IteratorAggregate");'
on your platform of choice too to check it!I'm utterly confused by what is happening here. Maybe I overlooked something important. But for some reason there is code in place to filter out
IteratorAggregate
and it doesn't work due to some very strange behavior in PCRE.