Skip to content

PHP8 specific removed create_function error #588

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/Rules/Functions/CallToNonExistentFunctionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleErrorBuilder;

Expand All @@ -18,13 +19,17 @@ class CallToNonExistentFunctionRule implements \PHPStan\Rules\Rule

private bool $checkFunctionNameCase;

private PhpVersion $phpVersion;

public function __construct(
ReflectionProvider $reflectionProvider,
bool $checkFunctionNameCase
bool $checkFunctionNameCase,
PhpVersion $phpVersion
)
{
$this->reflectionProvider = $reflectionProvider;
$this->checkFunctionNameCase = $checkFunctionNameCase;
$this->phpVersion = $phpVersion;
}

public function getNodeType(): string
Expand All @@ -39,6 +44,14 @@ public function processNode(Node $node, Scope $scope): array
}

if (!$this->reflectionProvider->hasFunction($node->name, $scope)) {
// @todo this feels a bit hacky, maybe we need a repository of
// functions that are removed based on version level?
if ($this->phpVersion->getVersionId() >= 80000 && (string) $node->name === 'create_function') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have that: https://github.com/JetBrains/phpstorm-stubs/blob/5cae90f74aa33a4cdbeb4da654525fe665e735c9/Core/Core.php#L755-L768

PHPStan should currently read the @removed annotation That's done here: https://github.com/ondrejmirtes/BetterReflection/blob/348a8777d11b9dd88f13e0d99a84d1671a8cf8a7/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php#L410-L417

But it's probably skipped or unused for functions. It works for classes+methods because RuntimeReflectionProvider is skipped in here:

if ($this->phpStormStubsSourceStubber->hasClass($className)) {
// check that userland class isn't aliased to the same name as a class from stubs
if (!class_exists($className, false)) {
return true;
}

But RuntimeReflectionProvider isn't skipped for functions:

public function hasFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope): bool
{
$has = $this->reflectionProvider->hasFunction($nameNode, $scope);
if (!$has) {
return false;
}
if ($this->singleReflectionInsteadOfFile === null) {
return true;
}
$functionReflection = $this->reflectionProvider->getFunction($nameNode, $scope);
if (!$functionReflection instanceof ReflectionWithFilename) {
return true;
}
return $functionReflection->getFileName() !== $this->singleReflectionInsteadOfFile;
}

Try to prototype some code in the last location to see if you get anywhere and the function is detected as removed.

You could try to check PhpStormStubsSourceStubber::isPresentFunction() === false which should say that the function no longer exists on PHP 8 and skip in that case.

return [
RuleErrorBuilder::message(sprintf('%s not found. This function has been DEPRECATED as of PHP 7.2.0, and REMOVED as of PHP 8.0.0. Use anonymous functions instead.', (string) $node->name))->build(),
];
}

return [
RuleErrorBuilder::message(sprintf('Function %s not found.', (string) $node->name))->discoveringSymbolsTip()->build(),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

namespace PHPStan\Rules\Functions;

use PHPStan\Php\PhpVersion;

/**
* @extends \PHPStan\Testing\RuleTestCase<CallToNonExistentFunctionRule>
*/
class CallToNonExistentFunctionRuleTest extends \PHPStan\Testing\RuleTestCase
{

/** @var int */
private $phpVersion = 70400;

protected function getRule(): \PHPStan\Rules\Rule
{
return new CallToNonExistentFunctionRule($this->createReflectionProvider(), true);
return new CallToNonExistentFunctionRule($this->createReflectionProvider(), true, new PhpVersion($this->phpVersion));
}

public function testEmptyFile(): void
Expand Down Expand Up @@ -97,4 +102,21 @@ public function testMatchExprAnalysis(): void
]);
}

public function testCallToCreateFunction(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}
// @todo The reflection provider doesn't respect PHP version to
// determine if a function does not exist. The function exists on 7
// but not 8. However, running on 7 it is still returned as existing.
// @see https://github.com/phpstan/phpstan/issues/5373
$this->phpVersion = 80000;
$this->analyse([__DIR__ . '/data/call-to-create-function.php'], [
[
'create_function not found. This function has been DEPRECATED as of PHP 7.2.0, and REMOVED as of PHP 8.0.0. Use anonymous functions instead.',
3,
],
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php

$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo $newfunc(2, M_E);