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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for PHP 8 #1072

Merged
merged 6 commits into from Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 11 additions & 15 deletions .travis.yml
@@ -1,20 +1,13 @@
language: php

matrix:
allow_failures:
- php: nightly
fast_finish: true
include:
- php: 5.6
env:
- DEPS=lowest
- php: 5.6
env:
- DEPS=latest
- php: 5.6
env:
- PHPUNIT=minimum
- DEPS=latest
- php: 7.0
env:
- DEPS=lowest
Expand Down Expand Up @@ -54,24 +47,27 @@ matrix:
- DEPS=latest
- COMPOSER_FLAGS="--ignore-platform-reqs"

install:
- if [[ $PHPUNIT == 'minimum' ]]; then sed -i 's/~5.7|/5.4.*|/g' ./composer.json ; fi
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update --no-interaction $COMPOSER_FLAGS ; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable --no-interaction $COMPOSER_FLAGS ; fi

before_script:
before_install:
# Install extensions for PHP 5.x series. 7.x includes them by default.
- |
if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then
composer self-update --2
if [[ $TRAVIS_PHP_VERSION == 5.6 ]]; then
cat <<< '
extension=mongo.so
extension=redis.so
' >> ~/.phpenv/versions/"$(phpenv version-name)"/etc/conf.d/travis.ini
fi
if [[ $TRAVIS_PHP_VERSION == "nightly" ]]; then
composer require --dev --no-update "phpunit/phpunit:^8.5|^9.0"
fi

install:
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update --no-interaction $COMPOSER_FLAGS ; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable --no-interaction $COMPOSER_FLAGS ; fi

script:
- |
if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then
if [[ $TRAVIS_PHP_VERSION == 5.6 ]]; then
./vendor/bin/phpunit --coverage-text --coverage-clover="build/logs/clover.xml" --testsuite="Mockery Test Suite PHP56";
else
./vendor/bin/phpunit --coverage-text --coverage-clover="build/logs/clover.xml" --testsuite="Mockery Test Suite";
Expand Down
4 changes: 3 additions & 1 deletion docs/mockery/configuration.rst
Expand Up @@ -45,7 +45,9 @@ ext/mongo's MongoCollection. Reflection cannot analyse the parameters of interna
classes. Most of the time, you never need to do this. It's mainly needed where an
internal class method uses pass-by-reference for a parameter - you MUST in such
cases ensure the parameter signature includes the ``&`` symbol correctly as Mockery
won't correctly add it automatically for internal classes.
won't correctly add it automatically for internal classes. Note that internal class
parameter overriding is not available in PHP 8. This is because incompatible
signatures have been reclassified as fatal errors.

Disabling reflection caching
----------------------------
Expand Down
2 changes: 2 additions & 0 deletions docs/mockery/gotchas.rst
Expand Up @@ -30,6 +30,8 @@ so it can be documented and resolved where possible. Here is a list to note:
pass by value on scalars and arrays). If references as internal class
method parameters are needed, you should use the
``\Mockery\Configuration::setInternalClassMethodParamMap()`` method.
Note, however that internal class parameter overriding is not available in
PHP 8 since incompatible signatures have been reclassified as fatal errors.

4. Creating a mock implementing a certain interface with incorrect case in the
interface name, and then creating a second mock implementing the same
Expand Down
18 changes: 11 additions & 7 deletions library/Mockery.php
Expand Up @@ -18,16 +18,17 @@
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

use Mockery\ClosureWrapper;
use Mockery\ExpectationInterface;
use Mockery\Generator\CachingGenerator;
use Mockery\Generator\Generator;
use Mockery\Generator\MockConfigurationBuilder;
use Mockery\Generator\MockNameBuilder;
use Mockery\Generator\StringManipulationGenerator;
use Mockery\Loader\EvalLoader;
use Mockery\Loader\Loader;
use Mockery\Matcher\MatcherAbstract;
use Mockery\ClosureWrapper;
use Mockery\Generator\MockNameBuilder;
use Mockery\Reflector;

class Mockery
{
Expand Down Expand Up @@ -74,6 +75,8 @@ public static function globalHelpers()

/**
* @return array
*
* @deprecated since 1.3.2 and will be removed in 2.0.
*/
public static function builtInTypes()
{
Expand All @@ -90,7 +93,7 @@ public static function builtInTypes()
'void',
);

if (version_compare(PHP_VERSION, '7.2.0-dev') >= 0) {
if (\PHP_VERSION_ID >= 70200) {
$builtInTypes[] = 'object';
}

Expand All @@ -100,6 +103,8 @@ public static function builtInTypes()
/**
* @param string $type
* @return bool
*
* @deprecated since 1.3.2 and will be removed in 2.0.
*/
public static function isBuiltInType($type)
{
Expand Down Expand Up @@ -858,7 +863,7 @@ private static function getNewDemeterMock(
) {
$newMockName = 'demeter_' . md5($parent) . '_' . $method;

if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
if (\PHP_VERSION_ID >= 70000) {
$parRef = null;
$parRefMethod = null;
$parRefMethodRetType = null;
Expand All @@ -870,13 +875,12 @@ private static function getNewDemeterMock(

if ($parRef !== null && $parRef->hasMethod($method)) {
$parRefMethod = $parRef->getMethod($method);
$parRefMethodRetType = $parRefMethod->getReturnType();
$parRefMethodRetType = Reflector::getReturnType($parRefMethod, true);

if ($parRefMethodRetType !== null) {
$nameBuilder = new MockNameBuilder();
$nameBuilder->addPart('\\' . $newMockName);
$type = PHP_VERSION_ID >= 70100 ? $parRefMethodRetType->getName() : (string)$parRefMethodRetType;
$mock = self::namedMock($nameBuilder->build(), $type);
$mock = self::namedMock($nameBuilder->build(), $parRefMethodRetType);
$exp->andReturn($mock);

return $mock;
Expand Down
6 changes: 5 additions & 1 deletion library/Mockery/Configuration.php
Expand Up @@ -113,6 +113,10 @@ public function mockingMethodsUnnecessarilyAllowed()
*/
public function setInternalClassMethodParamMap($class, $method, array $map)
{
if (\PHP_MAJOR_VERSION > 7) {
throw new \LogicException('Internal class parameter overriding is not available in PHP 8. Incompatible signatures have been reclassified as fatal errors.');
}

if (!isset($this->_internalClassParamMap[strtolower($class)])) {
$this->_internalClassParamMap[strtolower($class)] = array();
}
Expand All @@ -130,7 +134,7 @@ public function resetInternalClassMethodParamMaps()
/**
* Get the parameter map of an internal PHP class method
*
* @return array
* @return array|null
*/
public function getInternalClassMethodParamMap($class, $method)
{
Expand Down
4 changes: 1 addition & 3 deletions library/Mockery/Expectation.php
Expand Up @@ -203,9 +203,7 @@ private function throwAsNecessary($return)
return;
}

$type = version_compare(PHP_VERSION, '7.0.0') >= 0
? "\Throwable"
: "\Exception";
$type = \PHP_VERSION_ID >= 70000 ? "\Throwable" : "\Exception";

if ($return instanceof $type) {
throw $return;
Expand Down
38 changes: 11 additions & 27 deletions library/Mockery/Generator/Method.php
Expand Up @@ -20,8 +20,11 @@

namespace Mockery\Generator;

use Mockery\Reflector;

class Method
{
/** @var \ReflectionMethod */
private $method;

public function __construct(\ReflectionMethod $method)
Expand All @@ -34,40 +37,21 @@ public function __call($method, $args)
return call_user_func_array(array($this->method, $method), $args);
}

/**
* @return Parameter[]
*/
public function getParameters()
{
return array_map(function ($parameter) {
return array_map(function (\ReflectionParameter $parameter) {
return new Parameter($parameter);
}, $this->method->getParameters());
}

/**
* @return string|null
*/
public function getReturnType()
{
if (defined('HHVM_VERSION') && method_exists($this->method, 'getReturnTypeText') && $this->method->hasReturnType()) {
// Strip all return type for hhvm.
// eval() errors on hhvm return type include but not limited to
// tuple, ImmVector<>, ImmSet<>, ImmMap<>, array<>,
// anything with <>, void, mixed, this, and type-constant.
// For type-constant Can see https://docs.hhvm.com/hack/type-constants/introduction
// for more details.
return '';
}

if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0 && $this->method->hasReturnType()) {
$returnType = PHP_VERSION_ID >= 70100 ? $this->method->getReturnType()->getName() : (string) $this->method->getReturnType();

if ('self' === $returnType) {
$returnType = "\\" . $this->method->getDeclaringClass()->getName();
} elseif (!\Mockery::isBuiltInType($returnType)) {
$returnType = '\\' . $returnType;
}

if (version_compare(PHP_VERSION, '7.1.0-dev') >= 0 && $this->method->getReturnType()->allowsNull()) {
$returnType = '?' . $returnType;
}

return $returnType;
}
return '';
return Reflector::getReturnType($this->method);
}
}
2 changes: 1 addition & 1 deletion library/Mockery/Generator/MockConfigurationBuilder.php
Expand Up @@ -72,7 +72,7 @@ class MockConfigurationBuilder

public function __construct()
{
if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
if (\PHP_VERSION_ID >= 70000) {
$this->blackListedMethods = array_diff($this->blackListedMethods, $this->php7SemiReservedKeywords);
}
}
Expand Down
97 changes: 51 additions & 46 deletions library/Mockery/Generator/Parameter.php
Expand Up @@ -20,10 +20,14 @@

namespace Mockery\Generator;

use Mockery\Reflector;

class Parameter
{
private static $parameterCounter;
/** @var int */
private static $parameterCounter = 0;

/** @var \ReflectionParameter */
private $rfp;

public function __construct(\ReflectionParameter $rfp)
Expand All @@ -36,72 +40,73 @@ public function __call($method, array $args)
return call_user_func_array(array($this->rfp, $method), $args);
}

/**
* Get the reflection class for the parameter type, if it exists.
*
* This will be null if there was no type, or it was a scalar or a union.
*
* @return \ReflectionClass|null
*/
public function getClass()
{
return new DefinedTargetClass($this->rfp->getClass());
$typeHint = Reflector::getTypeHint($this->rfp, true);

return \class_exists($typeHint) ? DefinedTargetClass::factory($typeHint, false) : null;
}

public function getTypeHintAsString()
/**
* Get the string representation for the paramater type.
*
* @return string|null
*/
public function getTypeHint()
{
if (method_exists($this->rfp, 'getTypehintText')) {
// Available in HHVM
$typehint = $this->rfp->getTypehintText();

// not exhaustive, but will do for now
if (in_array($typehint, array('int', 'integer', 'float', 'string', 'bool', 'boolean'))) {
return '';
}

return $typehint;
}

if ($this->rfp->isArray()) {
return 'array';
}

/*
* PHP < 5.4.1 has some strange behaviour with a typehint of self and
* subclass signatures, so we risk the regexp instead
*/
if ((version_compare(PHP_VERSION, '5.4.1') >= 0)) {
try {
if ($this->rfp->getClass()) {
return $this->rfp->getClass()->getName();
}
} catch (\ReflectionException $re) {
// noop
}
}

if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0 && $this->rfp->hasType()) {
return PHP_VERSION_ID >= 70100 ? $this->rfp->getType()->getName() : (string) $this->rfp->getType();
}

if (preg_match('/^Parameter #[0-9]+ \[ \<(required|optional)\> (?<typehint>\S+ )?.*\$' . $this->rfp->getName() . ' .*\]$/', $this->rfp->__toString(), $typehintMatch)) {
if (!empty($typehintMatch['typehint'])) {
return $typehintMatch['typehint'];
}
}
return Reflector::getTypeHint($this->rfp);
}

return '';
/**
* Get the string representation for the paramater type.
*
* @return string
*
* @deprecated since 1.3.2 and will be removed in 2.0. Use getTypeHint() instead.
*/
public function getTypeHintAsString()
{
return (string) Reflector::getTypeHint($this->rfp, true);
}

/**
* Some internal classes have funny looking definitions...
* Get the name of the parameter.
*
* Some internal classes have funny looking definitions!
*
* @return string
*/
public function getName()
{
$name = $this->rfp->getName();
if (!$name || $name == '...') {
$name = 'arg' . static::$parameterCounter++;
$name = 'arg' . self::$parameterCounter++;
}

return $name;
}

/**
* Determine if the parameter is an array.
*
* @return bool
*/
public function isArray()
{
return Reflector::isArray($this->rfp);
}

/**
* Variadics only introduced in 5.6
* Determine if the parameter is variadic.
*
* @return bool
*/
public function isVariadic()
{
Expand Down