Skip to content

Commit

Permalink
Merge pull request #2287 from neos/albe-doctrine-php8
Browse files Browse the repository at this point in the history
TASK: Add basic support for PHP8
  • Loading branch information
albe authored Mar 22, 2021
2 parents 019f53e + c5268c8 commit 1f88b67
Show file tree
Hide file tree
Showing 18 changed files with 130 additions and 148 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.3', '7.4']
php-versions: ['7.3', '7.4', '8.0']
dependencies: ['highest']
composer-arguments: [''] # to run --ignore-platform-reqs in experimental builds
static-analysis: ['no']
Expand All @@ -27,8 +27,8 @@ jobs:
experimental: false
dependencies: 'highest'

# Experimental build for PHP 8
#- php-versions: '8.0'
# Experimental build for PHP nightly
#- php-versions: 'nightly'
# composer-arguments: '--ignore-platform-reqs'
# static-analysis: 'no'
# experimental: true
Expand Down
6 changes: 5 additions & 1 deletion Neos.Cache/Classes/Backend/FileBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,11 @@ public function flush(): void
{
Files::emptyDirectoryRecursively($this->cacheDirectory);
if ($this->frozen === true) {
@unlink($this->cacheDirectory . 'FrozenCache.data');
try {
@unlink($this->cacheDirectory . 'FrozenCache.data');
} catch (\Throwable $e) {
// PHP 8 apparently throws for unlink even with shutup operator, but we really don't care at this place. It's also the only way to handle this race-condition free.
}
$this->frozen = false;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Neos.Cache/Tests/Unit/Backend/FileBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ public function requireOnceDoesNotSwallowPhpWarningsOfTheIncludedFile()
$backend->setCache($mockCache);

$entryIdentifier = 'SomePhpEntryWithPhpWarning';
$backend->set($entryIdentifier, '<?php trigger_error("Warning!", E_WARNING); ?>');
$backend->set($entryIdentifier, '<?php trigger_error("Warning!", E_USER_WARNING); ?>');
$backend->requireOnce($entryIdentifier);
}

Expand All @@ -589,7 +589,7 @@ public function requireOnceDoesNotSwallowPhpNoticesOfTheIncludedFile()
$backend->setCache($mockCache);

$entryIdentifier = 'SomePhpEntryWithPhpNotice';
$backend->set($entryIdentifier, '<?php $undefined ++; ?>');
$backend->set($entryIdentifier, '<?php trigger_error("Notice!", E_USER_NOTICE); ?>');
$backend->requireOnce($entryIdentifier);
}

Expand Down
4 changes: 2 additions & 2 deletions Neos.Cache/Tests/Unit/Backend/SimpleFileBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ public function requireOnceDoesNotSwallowPhpWarningsOfTheIncludedFile()
$entryIdentifier = 'SomePhpEntryWithPhpWarning';

$simpleFileBackend = $this->getSimpleFileBackend();
$simpleFileBackend->set($entryIdentifier, '<?php trigger_error("Warning!", E_WARNING); ?>');
$simpleFileBackend->set($entryIdentifier, '<?php trigger_error("Warning!", E_USER_WARNING); ?>');
$simpleFileBackend->requireOnce($entryIdentifier);
}

Expand All @@ -418,7 +418,7 @@ public function requireOnceDoesNotSwallowPhpNoticesOfTheIncludedFile()
$entryIdentifier = 'SomePhpEntryWithPhpNotice';

$simpleFileBackend = $this->getSimpleFileBackend();
$simpleFileBackend->set($entryIdentifier, '<?php $undefined ++; ?>');
$simpleFileBackend->set($entryIdentifier, '<?php trigger_error("Notice!", E_USER_NOTICE); ?>');
$simpleFileBackend->requireOnce($entryIdentifier);
}

Expand Down
14 changes: 7 additions & 7 deletions Neos.Eel/Classes/Helper/MathHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ public function round($subject, $precision = 0)
if (!is_numeric($subject)) {
return NAN;
}
$subject = floatval($subject);
$subject = (float)$subject;
if ($precision != null && !is_int($precision)) {
return NAN;
}
Expand All @@ -422,14 +422,14 @@ public function round($subject, $precision = 0)
*/
public function sign($x)
{
if ($x < 0) {
if (!is_numeric($x)) {
return NAN;
} elseif ($x < 0) {
return -1;
} elseif ($x > 0) {
return 1;
} elseif ($x === 0 || $x === 0.0) {
return 0;
} else {
return NAN;
return 0;
}
}

Expand Down Expand Up @@ -492,9 +492,9 @@ public function trunc($x)
$sign = $this->sign($x);
switch ($sign) {
case -1:
return (int)ceil($x);
return (int)ceil((float)$x);
case 1:
return (int)floor($x);
return (int)floor((float)$x);
default:
return $sign;
}
Expand Down
19 changes: 16 additions & 3 deletions Neos.Flow/Classes/Core/LockManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,15 @@ protected function removeExpiredLock()
if (filemtime($this->lockFlagPathAndFilename) >= (time() - self::LOCKFILE_MAXIMUM_AGE)) {
return;
}
@unlink($this->lockFlagPathAndFilename);
@unlink($this->lockPathAndFilename);
try {
@unlink($this->lockFlagPathAndFilename);
} catch (\Throwable $e) {
// PHP 8 apparently throws for unlink even with shutup operator, but we really don't care at this place. It's also the only way to handle this race-condition free.
}
try {
@unlink($this->lockPathAndFilename);
} catch (\Throwable $e) {
}
}

/**
Expand Down Expand Up @@ -135,7 +142,13 @@ public function unlockSite()
fclose($this->lockResource);
unlink($this->lockPathAndFilename);
}
@unlink($this->lockFlagPathAndFilename);
if ($this->isSiteLocked()) {
try {
@unlink($this->lockFlagPathAndFilename);
} catch (\Throwable $e) {
// PHP 8 apparently throws for unlink even with shutup operator, but we really don't care at this place. It's also the only way to handle this race-condition free.
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,11 @@ protected function autowireProperties(array &$objectConfigurations)
continue;
}

$classMethodNames = get_class_methods($className);
try {
$classMethodNames = get_class_methods($className);
} catch (\TypeError $error) {
throw new UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371372);
}
if (!is_array($classMethodNames)) {
if (!class_exists($className)) {
throw new UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371);
Expand Down
6 changes: 5 additions & 1 deletion Neos.Flow/Classes/Package/PackageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,11 @@ protected function savePackageStates(array $orderedPackageStates): void
// Clean legacy file TODO: Remove at some point
$legacyPackageStatesPath = FLOW_PATH_CONFIGURATION . 'PackageStates.php';
if (is_file($legacyPackageStatesPath)) {
@unlink($legacyPackageStatesPath);
try {
@unlink($legacyPackageStatesPath);
} catch (\Throwable $e) {
// PHP 8 apparently throws for unlink even with shutup operator, but we really don't care at this place. It's also the only way to handle this race-condition free.
}
}
OpcodeCacheHelper::clearAllActive($this->packageInformationCacheFilePath);

Expand Down
13 changes: 10 additions & 3 deletions Neos.Flow/Classes/Property/TypeConverter/MediaTypeConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,19 @@ protected function convertMediaType($requestBody, $mediaType)
}
break;
case 'xml':
$entityLoaderValue = libxml_disable_entity_loader(true);
// TODO: Remove those lines once the minimum PHP version is 8.0
if (PHP_MAJOR_VERSION < 8) {
$entityLoaderValue = libxml_disable_entity_loader(true);
}
try {
$xmlElement = new \SimpleXMLElement(urldecode($requestBody), LIBXML_NOERROR);
libxml_disable_entity_loader($entityLoaderValue);
if (PHP_MAJOR_VERSION < 8) {
libxml_disable_entity_loader($entityLoaderValue);
}
} catch (\Exception $exception) {
libxml_disable_entity_loader($entityLoaderValue);
if (PHP_MAJOR_VERSION < 8) {
libxml_disable_entity_loader($entityLoaderValue);
}
return [];
}
$result = Arrays::convertObjectToArray($xmlElement);
Expand Down
4 changes: 2 additions & 2 deletions Neos.Flow/Classes/Reflection/MethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ public function getDescription()
}

/**
* @return string The name of a type (e.g. string, \stdClass) if it was declared as a return type, null otherwise
* @return string|null The name of a type (e.g. string, \stdClass) if it was declared as a return type, null otherwise
*/
public function getDeclaredReturnType()
{
if (!is_callable([$this, 'getReturnType'])) {
return null;
}
$type = $this->getReturnType();
return $type !== null ? (string)$type : null;
return $type !== null ? ltrim((string)$type, '?') : null;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions Neos.Flow/Classes/Reflection/ParameterReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ public function getClass()
}

/**
* @return string The name of a builtin type (e.g. string, int) if it was declared for the parameter (scalar type declaration), null otherwise
* @return string|null The name of a builtin type (e.g. string, int) if it was declared for the parameter (scalar type declaration), null otherwise
*/
public function getBuiltinType()
{
$type = $this->getType();
if ($type === null || !$type->isBuiltin()) {
if (!$type instanceof \ReflectionNamedType) {
return null;
}
return $type instanceof \ReflectionNamedType ? $type->getName() : (string)$type;
return $type->isBuiltin() ? $type->getName() : null;
}
}
23 changes: 11 additions & 12 deletions Neos.Flow/Classes/Reflection/ReflectionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -1788,9 +1788,6 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame
if ($parameter->isPassedByReference()) {
$parameterInformation[self::DATA_PARAMETER_BY_REFERENCE] = true;
}
if ($parameter->isArray()) {
$parameterInformation[self::DATA_PARAMETER_ARRAY] = true;
}
if ($parameter->isOptional()) {
$parameterInformation[self::DATA_PARAMETER_OPTIONAL] = true;
}
Expand All @@ -1800,11 +1797,20 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame

$parameterType = $parameter->getType();
if ($parameterType !== null) {
// TODO: This needs to handle ReflectionUnionType
$parameterType = ($parameterType instanceof \ReflectionNamedType) ? $parameterType->getName() : $parameterType->__toString();
}
if ($parameter->getClass() !== null) {
if ($parameterType !== null && !TypeHandling::isSimpleType($parameterType)) {
// We use parameter type here to make class_alias usage work and return the hinted class name instead of the alias
$parameterInformation[self::DATA_PARAMETER_CLASS] = $parameterType;
} elseif ($parameterType === 'array') {
$parameterInformation[self::DATA_PARAMETER_ARRAY] = true;
} else {
$builtinType = $parameter->getBuiltinType();
if ($builtinType !== null) {
$parameterInformation[self::DATA_PARAMETER_TYPE] = $builtinType;
$parameterInformation[self::DATA_PARAMETER_SCALAR_DECLARATION] = true;
}
}
if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
$parameterInformation[self::DATA_PARAMETER_DEFAULT_VALUE] = $parameter->getDefaultValue();
Expand All @@ -1816,17 +1822,10 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame
$parameterType = $this->expandType($method->getDeclaringClass(), $explodedParameters[0]);
}
}
if (!$parameter->isArray()) {
$builtinType = $parameter->getBuiltinType();
if ($builtinType !== null) {
$parameterInformation[self::DATA_PARAMETER_TYPE] = $builtinType;
$parameterInformation[self::DATA_PARAMETER_SCALAR_DECLARATION] = true;
}
}
if (!isset($parameterInformation[self::DATA_PARAMETER_TYPE]) && $parameterType !== null) {
$parameterInformation[self::DATA_PARAMETER_TYPE] = $this->cleanClassName($parameterType);
} elseif (!isset($parameterInformation[self::DATA_PARAMETER_TYPE])) {
$parameterInformation[self::DATA_PARAMETER_TYPE] = $parameter->isArray() ? 'array' : 'mixed';
$parameterInformation[self::DATA_PARAMETER_TYPE] = 'mixed';
}

return $parameterInformation;
Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Classes/SignalSlot/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public function dispatch(string $signalClassName, string $signalName, array $sig
}

foreach ($this->slots[$signalClassName][$signalName] as $slotInformation) {
$finalSignalArguments = $signalArguments;
$finalSignalArguments = array_values($signalArguments);
if (isset($slotInformation['object'])) {
$object = $slotInformation['object'];
} elseif (strpos($slotInformation['method'], '::') === 0) {
Expand Down
8 changes: 7 additions & 1 deletion Neos.Flow/Classes/Utility/PhpAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,14 @@ public function extractNamespace(): ?string
break;
}
list($type, $value) = $token;
if (defined('T_NAME_QUALIFIED') && $type === T_NAME_QUALIFIED) {
return $value;
}
if (defined('T_NAME_FULLY_QUALIFIED') && $type === T_NAME_FULLY_QUALIFIED) {
return ltrim($value, '\\');
}
if ($type === T_STRING) {
$namespaceParts[] = $value;
$namespaceParts[] = ltrim($value, '\\');
continue;
}
if ($type !== T_NS_SEPARATOR && $type !== T_WHITESPACE) {
Expand Down
20 changes: 19 additions & 1 deletion Neos.Flow/Tests/Functional/Mvc/ActionControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,6 @@ public function argumentTestsDataProvider()
'required date string' => ['requiredDate', '1980-12-13T14:22:12+02:00', '1980-12-13'],
'required date - missing value' => ['requiredDate', null, $requiredArgumentExceptionText],
'required date - mapping error' => ['requiredDate', 'no date', 'Validation failed while trying to call Neos\Flow\Tests\Functional\Mvc\Fixtures\Controller\ActionControllerTestBController->requiredDateAction().'],
'required date - empty value' => ['requiredDate', '', 'Uncaught Exception in Flow Argument 1 passed to Neos\Flow\Tests\Functional\Mvc\Fixtures\Controller\ActionControllerTestBController_Original::requiredDateAction() must be an instance of DateTime, null given'],
'optional date string' => ['optionalDate', '1980-12-13T14:22:12+02:00', '1980-12-13'],
'optional date - default value' => ['optionalDate', null, 'null'],
'optional date - mapping error' => ['optionalDate', 'no date', 'Validation failed while trying to call Neos\Flow\Tests\Functional\Mvc\Fixtures\Controller\ActionControllerTestBController->optionalDateAction().'],
Expand Down Expand Up @@ -460,6 +459,25 @@ public function argumentTests($action, $argument, $expectedResult)
self::assertTrue(strpos(trim($response->getBody()->getContents()), (string)$expectedResult) === 0, sprintf('The resulting string did not start with the expected string. Expected: "%s", Actual: "%s"', $expectedResult, $response->getBody()->getContents()));
}

/**
* @test
*/
public function requiredDateNullArgumentTest()
{
$arguments = [
'argument' => '',
];

$uri = str_replace('{@action}', 'requireddate', 'http://localhost/test/mvc/actioncontrollertestb/{@action}');
$response = $this->browser->request($uri, 'POST', $arguments);
if (PHP_MAJOR_VERSION < 8) {
$expectedResult = 'Uncaught Exception in Flow Argument 1 passed to Neos\Flow\Tests\Functional\Mvc\Fixtures\Controller\ActionControllerTestBController_Original::requiredDateAction() must be an instance of DateTime, null given';
} else {
$expectedResult = 'Uncaught Exception in Flow Neos\Flow\Tests\Functional\Mvc\Fixtures\Controller\ActionControllerTestBController_Original::requiredDateAction(): Argument #1 ($argument) must be of type DateTime, null given';
}
self::assertTrue(strpos(trim($response->getBody()->getContents()), (string)$expectedResult) === 0, sprintf('The resulting string did not start with the expected string. Expected: "%s", Actual: "%s"', $expectedResult, $response->getBody()->getContents()));
}

/**
* @test
*/
Expand Down
20 changes: 20 additions & 0 deletions Neos.Flow/Tests/Unit/SignalSlot/DispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,26 @@ public function dispatchPassesTheSignalArgumentsToTheSlotMethod(): void
self::assertSame(['bar', 'quux'], $arguments);
}

/**
* @test
*/
public function dispatchPassesUnnamedSignalArgumentsToTheSlotMethod(): void
{
$arguments = [];
$mockSlot = function () use (&$arguments) {
$arguments = func_get_args();
};

$mockObjectManager = $this->createMock(ObjectManagerInterface::class);

$dispatcher = new Dispatcher();
$dispatcher->connect('Foo', 'bar', $mockSlot, '', false);
$dispatcher->injectObjectManager($mockObjectManager);

$dispatcher->dispatch('Foo', 'bar', ['bar', 'quux']);
self::assertSame(['bar', 'quux'], $arguments);
}

/**
* @test
*/
Expand Down
6 changes: 5 additions & 1 deletion Neos.Utility.Files/Classes/Files.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@ public static function removeEmptyDirectoriesOnPath(string $path, string $basePa
break;
}
if (file_exists($path . '/.DS_Store')) {
@unlink($path . '/.DS_Store');
try {
@unlink($path . '/.DS_Store');
} catch (\Throwable $e) {
// PHP 8 apparently throws for unlink even with shutup operator, but we really don't care at this place. It's also the only way to handle this race-condition free.
}
}
if (@rmdir($path) === false) {
break;
Expand Down
Loading

0 comments on commit 1f88b67

Please sign in to comment.