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

Commit

Permalink
Improved empty value factory. Relates to #150, #151, #152, #153.
Browse files Browse the repository at this point in the history
  • Loading branch information
ezzatron committed Apr 29, 2016
1 parent 826dd52 commit b254a08
Show file tree
Hide file tree
Showing 11 changed files with 462 additions and 258 deletions.
1 change: 1 addition & 0 deletions src/Facade/FacadeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ protected function __construct(AssertionRecorder $assertionRecorder)
$nullEvent
);

$emptyValueFactory->setStubVerifierFactory($stubVerifierFactory);
$emptyValueFactory->setMockBuilderFactory($mockBuilderFactory);

$this->mockBuilderFactory = $mockBuilderFactory;
Expand Down
79 changes: 39 additions & 40 deletions src/Mock/Builder/MockBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public function __construct(
$featureDetector->isSupported('parser.relaxed-keywords');
$this->isEngineErrorExceptionSupported =
$featureDetector->isSupported('error.exception.engine');
$this->isDateTimeInterfaceSupported =
interface_exists('DateTimeInterface');

$this->factory = $factory;
$this->handleFactory = $handleFactory;
Expand Down Expand Up @@ -242,7 +244,7 @@ public function like($type)
}

foreach ($toAdd as $type) {
$name = $type->getName();
$name = strtolower($type->getName());

if (!isset($this->types[$name])) {
$this->types[$name] = $type;
Expand Down Expand Up @@ -601,61 +603,57 @@ public function source(MockGenerator $generator = null)

private function normalizeDefinition()
{
$isTraversable = false;
$isIterator = false;

foreach ($this->types as $type) {
if (
$type->implementsInterface('Iterator') ||
$type->implementsInterface('IteratorAggregate')
) {
$isIterator = true;

break;
}

if ($type->implementsInterface('Traversable')) {
$isTraversable = true;
}
}
$this->resolveInternalInterface(
'traversable',
'iterator',
'iteratoraggregate'
);

if ($isTraversable && !$isIterator) {
$this->types = array_merge(
array(
'IteratorAggregate' =>
new ReflectionClass('IteratorAggregate'),
),
$this->types
if ($this->isDateTimeInterfaceSupported) {
$this->resolveInternalInterface(
'datetimeinterface',
'datetimeimmutable',
'datetime'
);
}

if (!$this->isEngineErrorExceptionSupported) {
return; // @codeCoverageIgnore
if ($this->isEngineErrorExceptionSupported) {
$this->resolveInternalInterface('throwable', 'exception', 'error');
}
}

$isThrowable = false;
private function resolveInternalInterface(
$interface,
$preferred,
$alternate
) {
$isImplementor = false;
$isConcrete = false;

foreach ($this->types as $type) {
foreach ($this->types as $name => $type) {
if (
$type->isSubclassOf('Exception') || $type->isSubclassOf('Error')
$preferred === $name ||
$alternate === $name ||
$type->isSubclassOf($preferred) ||
$type->isSubclassOf($alternate)
) {
return;
}
$isConcrete = true;

$name = $type->getName();

if ('Exception' === $name || 'Error' === $type) {
return;
break;
}

if ($type->implementsInterface('Throwable')) {
$isThrowable = true;
if ($type->implementsInterface($interface)) {
$isImplementor = true;

if ($interface === $name) {
unset($this->types[$name]);
}
}
}

if ($isThrowable) {
if ($isImplementor && !$isConcrete) {
$this->types = array_merge(
array('Exception' => new ReflectionClass('Exception')),
array($preferred => new ReflectionClass($preferred)),
$this->types
);
}
Expand Down Expand Up @@ -707,6 +705,7 @@ private function define($definition)
private $isAnonymousClassSupported;
private $isRelaxedKeywordsSupported;
private $isEngineErrorExceptionSupported;
private $isDateTimeInterfaceSupported;
private $types;
private $parentClassName;
private $customMethods;
Expand Down
12 changes: 9 additions & 3 deletions src/Mock/Builder/MockDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ protected function buildMethods()
$unmockable = array();

if ($typeName = $this->parentClassName()) {
foreach ($this->types[$typeName]->getMethods() as $method) {
$type = $this->types[strtolower($typeName)];

foreach ($type->getMethods() as $method) {
if ($method->isPrivate()) {
continue;
}
Expand All @@ -303,7 +305,9 @@ protected function buildMethods()
$traitMethods = array();

foreach ($this->traitNames() as $typeName) {
foreach ($this->types[$typeName]->getMethods() as $method) {
$type = $this->types[strtolower($typeName)];

foreach ($type->getMethods() as $method) {
$methodName = $method->getName();
$methodDefinition =
new TraitMethodDefinition($method, $methodName);
Expand All @@ -323,7 +327,9 @@ protected function buildMethods()
}

foreach ($this->interfaceNames() as $typeName) {
foreach ($this->types[$typeName]->getMethods() as $method) {
$type = $this->types[strtolower($typeName)];

foreach ($type->getMethods() as $method) {
$methodName = $method->getName();

if (isset($unmockable[$methodName])) {
Expand Down
4 changes: 2 additions & 2 deletions src/Mock/MockGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ private function _callParent(

EOD;

$parentClass = $types[$parentClassName];
$parentClass = $types[strtolower($parentClassName)];

if ($constructor = $parentClass->getConstructor()) {
$constructorName = $constructor->getName();
Expand Down Expand Up @@ -721,7 +721,7 @@ private function _callParentConstructor(
$constructorTraitName = null;

foreach ($traitNames as $traitName) {
$trait = $types[$traitName];
$trait = $types[strtolower($traitName)];

if ($traitConstructor = $trait->getConstructor()) {
$constructor = $traitConstructor;
Expand Down
17 changes: 12 additions & 5 deletions src/Stub/EmptyValueFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Eloquent\Phony\Stub;

use Eloquent\Phony\Mock\Builder\MockBuilderFactory;
use EmptyIterator;
use ReflectionClass;
use ReflectionType;

Expand Down Expand Up @@ -45,6 +44,12 @@ public function __construct()
$functionReflector->hasMethod('getReturnType');
}

public function setStubVerifierFactory(
StubVerifierFactory $stubVerifierFactory
) {
$this->stubVerifierFactory = $stubVerifierFactory;
}

public function setMockBuilderFactory(
MockBuilderFactory $mockBuilderFactory
) {
Expand Down Expand Up @@ -72,12 +77,13 @@ public function fromType(ReflectionType $type)
case 'float': return .0;
case 'string': return '';
case 'array': return array();
case 'callable': return function () {};
case 'stdclass': return (object) array();

case 'traversable':
case 'iterator':
return new EmptyIterator();
case 'callable':
return $this->stubVerifierFactory->create();

case 'closure':
return function () {};

case 'generator':
$fn = function () { return; yield; };
Expand All @@ -89,6 +95,7 @@ public function fromType(ReflectionType $type)
}

private static $instance;
private $stubVerifierFactory;
private $mockBuilderFactory;
private $isReturnTypeSupported;
}
18 changes: 18 additions & 0 deletions test/src/Test/TestInterfaceH.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Phony package.
*
* Copyright © 2016 Erin Millard
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

namespace Eloquent\Phony\Test;

use DateTimeInterface;

interface TestInterfaceH extends DateTimeInterface
{
}
15 changes: 15 additions & 0 deletions test/suite/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ public function testMagicMethodReturnTypeMocking()
$this->assertSame('z', $mock->nonexistent());
}

public function testGeneratorReturnTypeSpying()
{
if (!$this->featureDetector->isSupported('return.type')) {
$this->markTestSkipped('Requires return type declarations.');
}
if (!$this->featureDetector->isSupported('generator')) {
$this->markTestSkipped('Requires generators.');
}

$stub = x\stub(eval('return function (): Generator {};'))->returns();
iterator_to_array($stub());

$stub->producedAll();
}

public function testReturnTypeMockingInvalidType()
{
if (!$this->featureDetector->isSupported('return.type')) {
Expand Down
Loading

0 comments on commit b254a08

Please sign in to comment.