Skip to content

Commit

Permalink
Closes #3956
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Nov 25, 2019
1 parent dfee47c commit ab5b024
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 191 deletions.
1 change: 1 addition & 0 deletions ChangeLog-9.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes of the PHPUnit 9.0 release series are documented in this fil
* Implemented [#3495](https://github.com/sebastianbergmann/phpunit/issues/3495): Remove `assertArraySubset()`
* Implemented [#3523](https://github.com/sebastianbergmann/phpunit/issues/3523): Remove the `setUseErrorHandler()` method
* Implemented [#3951](https://github.com/sebastianbergmann/phpunit/issues/3951): Remove optional parameters of `assertFileEquals()` etc.
* Implemented [#3956](https://github.com/sebastianbergmann/phpunit/issues/3956): Remove support for doubling multiple interfaces
* Implemented [#3957](https://github.com/sebastianbergmann/phpunit/issues/3957): Remove `expectExceptionMessageRegExp()`

[9.0.0]: https://github.com/sebastianbergmann/phpunit/compare/8.5...master
Expand Down
161 changes: 25 additions & 136 deletions src/Framework/MockObject/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,12 @@ final class Generator
/**
* Returns a mock object for the specified class.
*
* @param string|string[] $type
* @param null|array $methods
* @param null|array $methods
*
* @throws RuntimeException
*/
public function getMock($type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false, object $proxyTarget = null, bool $allowMockingUnknownTypes = true, bool $returnValueGeneration = true): MockObject
public function getMock(string $type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false, object $proxyTarget = null, bool $allowMockingUnknownTypes = true, bool $returnValueGeneration = true): MockObject
{
if (!\is_array($type) && !\is_string($type)) {
throw InvalidArgumentException::create(1, 'array or string');
}

if (!\is_array($methods) && null !== $methods) {
throw InvalidArgumentException::create(2, 'array');
}
Expand All @@ -66,46 +61,15 @@ public function getMock($type, $methods = [], array $arguments = [], string $moc
$type = 'Iterator';
}

if (\is_array($type)) {
$type = \array_unique(
\array_map(
static function ($type) {
if ($type === 'Traversable' ||
$type === '\\Traversable' ||
$type === '\\Iterator') {
return 'Iterator';
}

return $type;
},
if (!$allowMockingUnknownTypes && !\class_exists($type, $callAutoload) && !\interface_exists($type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock class or interface "%s" which does not exist',
$type
)
);
}

if (!$allowMockingUnknownTypes) {
if (\is_array($type)) {
foreach ($type as $_type) {
if (!\class_exists($_type, $callAutoload) &&
!\interface_exists($_type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock class or interface "%s" which does not exist',
$_type
)
);
}
}
} elseif (!\class_exists($type, $callAutoload) && !\interface_exists($type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock class or interface "%s" which does not exist',
$type
)
);
}
}

if (null !== $methods) {
foreach ($methods as $method) {
if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', (string) $method)) {
Expand Down Expand Up @@ -316,12 +280,8 @@ public function getObjectForTrait(string $traitName, string $traitClassName = ''
);
}

public function generate($type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass
public function generate(string $type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass
{
if (\is_array($type)) {
\sort($type);
}

if ($mockClassName !== '') {
return $this->generateMock(
$type,
Expand All @@ -335,7 +295,7 @@ public function generate($type, array $methods = null, string $mockClassName = '
}

$key = \md5(
\is_array($type) ? \implode('_', $type) : $type .
$type .
\serialize($methods) .
\serialize($callOriginalClone) .
\serialize($cloneArguments) .
Expand Down Expand Up @@ -595,11 +555,9 @@ private function getObject(MockType $mockClass, $type = '', bool $callOriginalCo
}

/**
* @param array|string $type
*
* @throws RuntimeException
*/
private function generateMock($type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass
private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass
{
$classTemplate = $this->getTemplate('mocked_class.tpl');
$additionalInterfaces = [];
Expand All @@ -610,85 +568,23 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
$class = null;
$mockMethods = new MockMethodSet;

if (\is_array($type)) {
$interfaceMethods = [];

foreach ($type as $_type) {
if (!\interface_exists($_type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Interface "%s" does not exist.',
$_type
)
);
}

$additionalInterfaces[] = $_type;

try {
$typeClass = new \ReflectionClass($_type);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd

foreach ($this->getClassMethods($_type) as $methodTrait) {
if (\in_array($methodTrait, $interfaceMethods, true)) {
throw new RuntimeException(
\sprintf(
'Duplicate method "%s" not allowed.',
$methodTrait
)
);
}

try {
$methodReflection = $typeClass->getMethod($methodTrait);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd

if ($this->canMockMethod($methodReflection)) {
$mockMethods->addMethods(
MockMethod::fromReflection($methodReflection, $callOriginalMethods, $cloneArguments)
);

$interfaceMethods[] = $methodTrait;
}
}
}

unset($interfaceMethods);
}

$mockClassName = $this->generateClassName(
$_mockClassName = $this->generateClassName(
$type,
$mockClassName,
'Mock_'
);

if (\class_exists($mockClassName['fullClassName'], $callAutoload)) {
if (\class_exists($_mockClassName['fullClassName'], $callAutoload)) {
$isClass = true;
} elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) {
} elseif (\interface_exists($_mockClassName['fullClassName'], $callAutoload)) {
$isInterface = true;
}

if (!$isClass && !$isInterface) {
$prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
$prologue = 'class ' . $_mockClassName['originalClassName'] . "\n{\n}\n\n";

if (!empty($mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $mockClassName['namespaceName'] .
if (!empty($_mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $_mockClassName['namespaceName'] .
" {\n\n" . $prologue . "}\n\n" .
"namespace {\n\n";

Expand All @@ -698,7 +594,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
$mockedCloneMethod = true;
} else {
try {
$class = new \ReflectionClass($mockClassName['fullClassName']);
$class = new \ReflectionClass($_mockClassName['fullClassName']);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
Expand All @@ -713,7 +609,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
throw new RuntimeException(
\sprintf(
'Class "%s" is declared "final" and cannot be mocked.',
$mockClassName['fullClassName']
$_mockClassName['fullClassName']
)
);
}
Expand All @@ -736,7 +632,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
}
// @codeCoverageIgnoreEnd

foreach ($this->getInterfaceOwnMethods($mockClassName['fullClassName']) as $methodTrait) {
foreach ($this->getInterfaceOwnMethods($_mockClassName['fullClassName']) as $methodTrait) {
$methodName = $methodTrait->getName();

if ($class->hasMethod($methodName)) {
Expand All @@ -762,7 +658,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
);
}

$mockClassName = $this->generateClassName(
$_mockClassName = $this->generateClassName(
$actualClassName,
'',
'Mock_'
Expand Down Expand Up @@ -808,7 +704,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
if ($explicitMethods === [] &&
($isClass || $isInterface)) {
$mockMethods->addMethods(
...$this->mockClassMethods($mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)
...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)
);
}

Expand All @@ -835,7 +731,7 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
} else {
$mockMethods->addMethods(
MockMethod::fromName(
$mockClassName['fullClassName'],
$_mockClassName['fullClassName'],
$methodName,
$cloneArguments
)
Expand Down Expand Up @@ -873,33 +769,26 @@ private function generateMock($type, ?array $explicitMethods, string $mockClassN
'prologue' => $prologue ?? '',
'epilogue' => $epilogue ?? '',
'class_declaration' => $this->generateMockClassDeclaration(
$mockClassName,
$_mockClassName,
$isInterface,
$additionalInterfaces
),
'clone' => $cloneTrait,
'mock_class_name' => $mockClassName['className'],
'mock_class_name' => $_mockClassName['className'],
'mocked_methods' => $mockedMethods,
'method' => $methodTrait,
]
);

return new MockClass(
$classTemplate->render(),
$mockClassName['className'],
$_mockClassName['className'],
$configurable
);
}

/**
* @param array|string $type
*/
private function generateClassName($type, string $className, string $prefix): array
private function generateClassName(string $type, string $className, string $prefix): array
{
if (\is_array($type)) {
$type = \implode('_', $type);
}

if ($type[0] === '\\') {
$type = \substr($type, 1);
}
Expand Down
Loading

0 comments on commit ab5b024

Please sign in to comment.