-
Notifications
You must be signed in to change notification settings - Fork 132
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
[PHP 7.1] Add support for ReflectionClassConstant (#281) #282
Conversation
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.
String cast and static export aren't covered by tests
src/Reflection/ReflectionClass.php
Outdated
|
||
class ReflectionClass implements Reflection, \Reflector | ||
{ | ||
|
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.
Revert
src/Reflection/ReflectionClass.php
Outdated
$constants = $this->getReflectionConstants(); | ||
|
||
if (!isset($constants[$name])) { | ||
return 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.
I'd rather have an exception here. null
makes it a silent failure
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.
In original php-reflection this method returns false:
https://github.com/php-src/php/blob/master/ext/reflection/php_reflection.c#L4547
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.
Urgh, that's just horrible :-(
src/Reflection/ReflectionClass.php
Outdated
{ | ||
$constants = $this->getReflectionConstants(); | ||
|
||
if (!isset($constants[$name])) { |
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.
array_key_exists()
src/Reflection/ReflectionClass.php
Outdated
|
||
$constants = []; | ||
|
||
foreach ($this->node->stmts as $stmt) { |
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.
Use an array filter here. A private method where you do it would be best
src/Reflection/ReflectionClass.php
Outdated
} | ||
} | ||
|
||
$this->cachedReflectionConstants = $constants; |
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.
Can return
directly on this assignment
* | ||
* @return bool | ||
*/ | ||
public function isProtected() |
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.
Return hint. Docblock is redundant
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.
Would you be for adding coding standard for these?
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.
If a return type can be specified, it must be specified
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 agree! There is a sniff for that that can check (and complete) that for you.
{ | ||
if ($this->isPublic()) { | ||
return '<public>'; | ||
} elseif ($this->isPrivate()) { |
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.
else
not needed
return '<public>'; | ||
} elseif ($this->isPrivate()) { | ||
return '<private>'; | ||
} elseif ($this->isProtected()) { |
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.
Same
} elseif ($this->isProtected()) { | ||
return '<protected>'; | ||
} | ||
return '<unknown>'; |
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.
Why the brackets?
{ | ||
private function getComposerLocator() : ComposerSourceLocator | ||
{ | ||
global $loader; |
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.
Really smelly...
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.
Yep, is really smelly code, but...
https://github.com/Roave/BetterReflection/blob/master/test/unit/Reflection/ReflectionClassTest.php#L44 :(
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.
Can you try $loader = require __DIR__ . '/../../../lots-of-../../vendor/autoload.php';
?
It's just a test that checks that all method calls go through to an underlying better-reflection class. It's just there for API compatibility: if php-src defines new reflection methods, this test catches them. |
Also need:
I'll add it later |
Huh, I think I'm done. |
src/Reflection/ReflectionClass.php
Outdated
public function getReflectionConstants() : array | ||
{ | ||
if (null === $this->cachedReflectionConstants) { | ||
$this->cachedReflectionConstants = array_reduce( |
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.
Just noticed that this code does not consider parent classes and traits - anything that we can do there?
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 native reflection not provide access for constants inherited from parents. Current behavior is exactly the same as in the native reflection.
class A
{
const A = 1;
}
class B
{
const B = 2;
}
foreach ([A::class, B::class] as $class) {
echo 'Native ', $class, ':', PHP_EOL;
$ref = new \ReflectionClass($class);
foreach ($ref->getReflectionConstants() as $const) {
echo $const;
}
echo 'Better ', $class, ':', PHP_EOL;
$ref = $reflector->reflect($class);
foreach ($ref->getReflectionConstants() as $const) {
echo $const;
}
}
Output:
Native A:
Constant [ public integer A ] { 1 }
Better A:
Constant [ public integer A ] { 1 }
Native B:
Constant [ public integer B ] { 2 }
Better B:
Constant [ public integer B ] { 2 }
And traits doesn't support constants.
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 native reflection not provide access for constants inherited from parents. Current behavior is exactly the same as in the native reflection.
We should provide a "clean" implementation in our own code. For instance, we have Roave\BetterReflection\Reflection\ReflectionClass#getImmediateMethods()
for methods implemented only at the current inheritance level (without considering ancestors), and then we provide a reflection-compatible API that retrieves all methods, exactly like internal reflection. The same should apply here.
And traits doesn't support constants.
Didn't know this, thanks for clarifying!
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 I added new methods and class to reflection-compat API.
But new features for class constants is not related to this PR, IMHO.
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.
But new features for class constants is not related to this PR, IMHO.
It's just in line with the package style: it's Better Reflection, not just reflection ;-)
@Ocramius We depend on this PR in ApiGen and after 2 weeks there is probably something missing. What needs to be done here? |
@TomasVotruba I simply didn't see the pushes. A rebase is needed, then another round of reviews |
Hmmm... Strange conflict... I check this tomorrow. |
ReflectionClass::getReflectionConstants() was refactored
…::getComposerLocator()
…Node() Added ::getModifiers(), ::getDeclaringClass() and ::getDocComment()
9a7de41
to
2ee8f98
Compare
@Ocramius rebased 2 days ago ;) |
*/ | ||
public function getDocComment() : string | ||
{ | ||
if (!$this->node->hasAttribute('comments')) { |
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.
Please see #284 about the contents of this method
use \ReflectionClassConstant as CoreReflectionClassConstant; | ||
use Roave\BetterReflection\Reflection\ReflectionClassConstant as BetterReflectionClassConstant; | ||
|
||
class ReflectionClassConstant extends CoreReflectionClassConstant |
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->betterClassConstant = $betterClassConstant; | ||
} | ||
|
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.
Empty line to be removed
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.
Resolved
|
||
class ReflectionClassConstant extends CoreReflectionClassConstant | ||
{ | ||
private $betterClassConstant; |
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.
Docblock
src/Reflection/ReflectionClass.php
Outdated
return null; | ||
} | ||
|
||
return $constants[$name]; |
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.
return $this->getReflectionConstants()[$name] ?? null
private $valueCached = false; | ||
|
||
/** | ||
* @var mixed const value |
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.
Constants are not mixed: they can contain int|float|array|string|bool|null
- all related docblocks need alignment to this too
*/ | ||
public function getValue() | ||
{ | ||
if (false === $this->valueCached) { |
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.
Consider flipping over the conditional, so the processing comes after an early return
*/ | ||
public function getModifiers() : int | ||
{ | ||
return $this->node->flags === 0 ? Class_::MODIFIER_PUBLIC : $this->node->flags; |
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.
Is 0
considered public
at all times?
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.
Maybe we should store the correct modifier if no modifier is defined (public) instead? How much does that go against the original reflection API?
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.
If modifier not provided (just const A = 'b'
) visibility is public.
Original behavior I'll check tomorrow.
*/ | ||
public function getDeclaringClass() : ReflectionClass | ||
{ | ||
return $this->owner; |
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.
What about non-class constants?
<?php
namespace Foo {
const BAR = 'baz';
}
namespace {
var_dump(\Foo\BAR);
}
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.
PHP doesn't have reflection for constants.
ReflectionClassConstant can be instantiated only on ReflectionClass and only for constant related to "this" class.
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 was further discussed later on. Indeed, further work on non-class constants will not land in this patch 👍
if ($this->isProtected()) { | ||
return 'protected'; | ||
} | ||
return ''; |
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.
Should we even consider this scenario?
…ve reflection behavior
…return `public` as default
+1 week... :( |
@Ocramius this is specifically to do with class constants, so not worried about My onl concern here is if a new API is introduced into PHP that allows reflection of constants (whether specific to class constants like this) or even all "forms" of constants. This API would then conflict with this, so if that were the case, we'd need to release a new major as BC would be broken. Apart from that 👍 from me. |
Constant [ integer MY_CONST_1 ] { 123 } | ||
Constant [ integer MY_CONST_2 ] { 234 } | ||
- Constants [5] { | ||
Constant [ public integer MY_CONST_1 ] { 123 } |
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.
Does this information exist when exporting a reflection in core PHP?
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.
Yes
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.
👍
src/Reflection/ReflectionClass.php
Outdated
|
||
return $this->cachedReflectionConstants = array_reduce( | ||
$this->node->stmts, | ||
[$this, 'processReflectionConstants'], |
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 method can just be inlined here I reckon, it's only used the once and I can't see it being re-usable?
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.
Done
We would simply NOT release that part as a |
Actually, that's a good point. The Adapter class for this is totally unnecessary at this time :) |
@asgrim can you create an issue for the namespaced (non-class) constant then? Not to be tackled here at this point. |
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 👍
Thanks @POPSuL! I'm merging after applying a few nitpicks :-)
* | ||
* @return ReflectionClassConstant[] | ||
*/ | ||
public function getReflectionConstants() : array |
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.
Note to self: this block is to be changed at merge time (not a reduce operation)
*/ | ||
public function getDeclaringClass() : ReflectionClass | ||
{ | ||
return $this->owner; |
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 was further discussed later on. Indeed, further work on non-class constants will not land in this patch 👍
* | ||
* @link http://php.net/manual/en/reflector.export.php | ||
* @return string | ||
* @since 5.0 |
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.
Note to self: fix comment on merge
* | ||
* @link http://php.net/manual/en/reflector.tostring.php | ||
* @return string | ||
* @since 5.0 |
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.
Note to self: fix comment on merge
Just referencing it for confused folks: |
Ah yes, confused me indeed. Couldn't find documentation, so disregard my earlier comment about removing the Adapter. Does the API definitely match? How do we verify compatibility without checking the src? |
@asgrim there is a |
…array_map` and `array_combine` sequence
* [WIP] Initial support for ReflectionConstant from Roave/BetterReflection#282 * [WIP] Added descriptions and deprecations for constants * [WIP] Fixed broken tests * Added constants to stub * Fixed ClassConstantReflection::getAnnotations() and cs * Fixed tests and cs * Implemented getStart/EndLine for class constants and properties
#281
Note: I did not understand how testReflectionObjectOverridesAllMethodsInReflectionClass works, therefore I missed it.