-
Notifications
You must be signed in to change notification settings - Fork 130
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
ReflectionParameter
: try to get the correct default value for global constants
#678
Conversation
@@ -187,7 +188,7 @@ private function parseDefaultValueNode(): void | |||
) { | |||
$this->isDefaultValueConstant = true; | |||
$this->defaultValueConstantName = $defaultValueNode->name->parts[0]; | |||
$this->defaultValue = null; | |||
$this->defaultValue = constant($this->defaultValueConstantName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not really acceptable, since constant()
leads to autoloading here:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Ocramius Node\Expr\ConstFetch
is only for global constants not for class constants, instead of Node\Expr\ClassConstFetch
, or? If this is correct then there is no autoloading here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but then it will crash in case of an undefined constant: https://3v4l.org/ZZnmJ
Looking at our code, we do indeed use constant()
elsewhere (in Roave\BetterReflection\NodeCompiler\CompileNodeToValue
)
I'd say that this patch is good if following is true:
- tests are added
CompileNodeToValue
is used in here, instead ofconstant()
Having a constant()
lookup there is already a bug: let's keep it isolated for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Ocramius constant()
is replaced and a simple test case is added
But now I see Roave\BetterReflection\NodeCompiler\Exception\UnableToCompileNode : Could not locate constant "SOME_DEFINED_VALUE" ...
in a different test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test that fails is this one:
public function testIsDefaultValueConstantAndGetDefaultValueConstantName() : void
{
$classInfo = $this->reflector->reflect(Methods::class);
$method = $classInfo->getMethod('methodWithUpperCasedDefaults');
$boolUpper = $method->getParameter('boolUpper');
self::assertFalse($boolUpper->isDefaultValueConstant());
$boolLower = $method->getParameter('boolLower');
self::assertFalse($boolLower->isDefaultValueConstant());
$nullUpper = $method->getParameter('nullUpper');
self::assertFalse($nullUpper->isDefaultValueConstant());
$method = $classInfo->getMethod('methodWithConstAsDefault');
$constDefault = $method->getParameter('constDefault');
self::assertTrue($constDefault->isDefaultValueConstant());
self::assertSame(Methods::class . '::SOME_CONST', $constDefault->getDefaultValueConstantName());
$definedDefault = $method->getParameter('definedDefault');
self::assertTrue($definedDefault->isDefaultValueConstant());
self::assertSame('SOME_DEFINED_VALUE', $definedDefault->getDefaultValueConstantName());
$intDefault = $method->getParameter('intDefault');
self::assertFalse($intDefault->isDefaultValueConstant());
$this->expectException(LogicException::class);
$this->expectExceptionMessage('This parameter is not a constant default value, so cannot have a constant name');
$intDefault->getDefaultValueConstantName();
}
The source is Methods.php
:
<?php
namespace Roave\BetterReflectionTest\Fixture;
\define('SOME_DEFINED_VALUE', 1);
abstract class Methods
{
const SOME_CONST = 1;
public function __construct()
{
}
public function publicMethod()
{
}
private function privateMethod()
{
}
protected function protectedMethod()
{
}
final public function finalPublicMethod()
{
}
abstract public function abstractPublicMethod();
public static function staticPublicMethod()
{
}
function noVisibility()
{
}
public function __destruct()
{
}
/**
* @param string $parameter1
* @param int|float $parameter2
*/
public function methodWithParameters($parameter1, $parameter2)
{
}
public function methodWithOptionalParameters($parameter, $optionalParameter = null)
{
}
public function methodWithExplicitTypedParameters(
\stdClass $stdClassParameter,
ClassForHinting $namespaceClassParameter,
\Roave\BetterReflectionTest\Fixture\ClassForHinting $fullyQualifiedClassParameter,
array $arrayParameter,
callable $callableParameter
) {
}
public function methodWithVariadic($nonVariadicParameter, ...$variadicParameter)
{
}
public function methodWithReference($nonRefParameter, &$refParameter)
{
}
public function methodWithNonOptionalDefaultValue($firstParameter = 'someValue', $secondParameter)
{
}
public function methodToCheckAllowsNull($allowsNull, \stdClass $hintDisallowNull, \stdClass $hintAllowNull = null)
{
}
public function methodWithConstAsDefault($intDefault = 1, $constDefault = self::SOME_CONST, $definedDefault = SOME_DEFINED_VALUE)
{
}
public function methodWithUpperCasedDefaults($boolUpper = TRUE, $boolLower = false, $nullUpper = NULL)
{
}
}
I wonder if we can make the default value "lazy", so that we only trip the exception if a read on the constant is happening. The test above is indeed correct: the Methods.php
file should never be included, and therefore SOME_DEFINED_VALUE
cannot be found by constant()
.
@@ -449,7 +466,10 @@ public function isDefaultValueConstant(): bool | |||
*/ | |||
public function getDefaultValueConstantName(): string | |||
{ | |||
$this->parseDefaultValueNode(); | |||
if ($this->defaultValueAst === null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these AST caches necessary? I would suggest avoiding them, if they don't bring a massive advantage (the added complexity is noticeable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remvoed the AST cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think CS is needed (run vendor/bin/phpcbf
), and maybe we should remove some conditionals that aren't strictly necessary for the patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Some wonkiness in the tests, but I'm OK with ignoring CS for those couple rows :-)
Good job!
ReflectionParameter
: try to get the correct default value for global constants
For example, this function
function foo(int $case = \CASE_LOWER);
, ReflectionParameter->allowsNull() for$case
will currently returntrue
.This change is![Reviewable](https://camo.githubusercontent.com/23b05f5fb48215c989e92cc44cf6512512d083132bd3daf689867c8d9d386888/68747470733a2f2f72657669657761626c652e696f2f7265766965775f627574746f6e2e737667)