Skip to content

Conversation

mbeccati
Copy link
Contributor

A work based off of Sara's https://wiki.php.net/rfc/reflectionparameter.typehint with a twist.

I've changed ReturnTypeAnnotation back to ReturnTypeHint, as this is still what we're calling them.

The new methods are:

  • ReflectionFunctionAbstract::hasReturnTypeHint()
  • ReflectionFunctionAbstract::getReturnTypeHint()
  • ReflectionTypeHint::isInstance()
  • ReflectionTypeHint::isScalar()

References:
http://markmail.org/message/meyx6t52opv7xfbo

Btw, not trying to bypass the RFC process, but atm I don't have time to propose one.

@smalyshev
Copy link
Contributor

Please do not propagate the misnomer "type hint". There's nothing of "hint" in type definition. We've made this mistake once, but we should not make it in perpetuity. We should phase out broken terminology instead.

@smalyshev smalyshev added the RFC label Mar 22, 2015
@Kubo2
Copy link
Contributor

Kubo2 commented Mar 22, 2015

@smalyshev:

So how would you call it then? The thing it accomplishes isn't actually done by presence of an annotation in code (or do I understand it wrong?).

@mbeccati
Copy link
Contributor Author

@smalyshev Sure. However, I thought we settled for "hint" though, as that's the jargon that's been used extensively during the recent Scalar Type Hints civil war ;)

@nikic
Copy link
Member

nikic commented Apr 1, 2015

Looks okay with TypeHint changed back to TypeAnnotation. @morrisonlevi Thoughts?

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 1, 2015

@nikic isn't TypeAnnotation a bit too close to Annotations, which aren't supported yet, but something very different?

@nikic
Copy link
Member

nikic commented Apr 1, 2015

@mbeccati I don't really think that there's much potential for confusion in that direction. But, maybe just call it ReflectionType? Would also be a bit more general, so it could be reused for e.g. generic type arguments or whatever.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 1, 2015

@nikic sure. But maybe we should move the discussion to internals and/or resurrect the RFC?

@nikic
Copy link
Member

nikic commented Apr 1, 2015

@mbeccati Yeah, feel free to bring it up on internals again. If there is no disagreement over the APIs I hope we can land this without dragging through the whole RFC cycle.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 1, 2015

@nikic to be honest I tried a couple of times already without gathering any attention. Maybe if it comes from you we have better chances to land it ;)

@morrisonlevi
Copy link
Contributor

If you look at the RFC history I've probably edited it more than Sara has :)

Personally, I don't think we should include isNull, isScalar, etc. Furthermore, there is a proposal out for union types would would basically require a new API again should it pass.

I honestly think our best solution is to ship without reflection support. Having a bad Reflection API is worse than not having one at all in my opinion; I mean this constructively. Our current API is not very good and this PR basically continues down the same path and just adds more methods.

@morrisonlevi
Copy link
Contributor

Some concrete feedback on this PR:

  • I don't think isInstance is appropriately named. I'm not sure isObject or isClass are any better. It's somewhat shocking to me that there isn't a well-known term for something that is either a class or an interface.
  • Why is there isScalar instead of multiple options, such as isInt, isFloat, etc?
  • I'm not sure why you created typehint_reference here; can you explain that?

@nikic
Copy link
Member

nikic commented Apr 1, 2015

@morrisonlevi I don't think shipping without reflection is an option. I've already hit an issue with a mocking framework generating invalid code because it does not respect scalar and return typehints (due to lack of reflection support for them). Reflection is not just cosmetics.

Furthermore, I do not see why this would be incompatible with union types. For them isInstance(), isArray(), etc return false and isUnion() returns true - and getUnionTypes() or whatever returns an array of ReflectionTypes representing the individual members in the union. Similar handling is possible for generics.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 1, 2015

@morrisonlevi:

  • following your reasoning, then instanceof is not appropriately named either.
  • (string)$reflectionType already returns "int", "string", etc... why would we need specific isWhatever methods?
  • I've reused Sara's code and expanded from there, so I can't explain that myself.

@nikic
Copy link
Member

nikic commented Apr 1, 2015

@mbeccati Regarding the second point, you are of course right. However this begs the question why there are isArray() and isCallable() methods as these can also be checked by comparing the type string. Maybe we should drop those two and stick with just __toString(), isNullable() and isInstance()?

class ReflectionType {
    function __toString() : string;
    function isNullable() : bool;
    function isInstance() : bool;
}

@nikic
Copy link
Member

nikic commented Apr 1, 2015

After further discussion, the isInstance() method could be problematic if we introduce an object type in the future. If isInstance() is used to distinguish between "special" types and class/interface types, you wouldn't want it to return true for object - but that would be weird given it's name.

As such the suggestion was to call it isClassOrInterface() instead - we don't need to save characters, better be explicit.

@nikic
Copy link
Member

nikic commented Apr 1, 2015

So, to summarize the current room 11 consensus:

class ReflectionType {
    function __toString(): string;
    function isClassOrInterface(): bool;
    function allowsNull(): bool;
}

Another topic that came up is what __toString() returns for hints against self and parent. Your current implementation probably returns them directly, while it would likely be better to return the resolved names. Otherwise people will likely just have to manually resolve them. Furthermore this would allow us to resolve these names at compile-time in the future.

case IS_DOUBLE: RETURN_STRINGL("float", sizeof("float") - 1);
default:
php_error_docref(NULL, E_ERROR, "Unknown type hint: %d", (int)param->arg_info->type_hint);
RETURN_EMPTY_STRING();
Copy link
Member

Choose a reason for hiding this comment

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

Should use EMPTY_SWITCH_DEFAULT_CASE() here instead of the E_ERROR to assert that the switch enumerates all options.

@nikic nikic added the PHP7 label Apr 2, 2015
@mbeccati mbeccati force-pushed the reflection-typehints-sth-v5 branch from 322f877 to a403608 Compare April 3, 2015 07:24
@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 3, 2015

@nikic I've fixed internal class support and EMPTY_SWITCH_DEFAULT_CASE().
@morrisonlevi now there's a reason to have a typehint_reference (we need both arg_info and fptr)

Re: room11 consensus, is that a thing? Internals or it didn't happen ;)

Seriously though that seems quite a big deviation from the original RFC, but I'll be happy to make the required changes if there's consensus also outside room11. About self and parent, I'm not sure. To be honest I don't actually like __toString() being the main way to get the type name; I also feel that it'd be useful to have a way to get both self/parent and the acual resolved class name if required.

@morrisonlevi
Copy link
Contributor

To clarify the Room 11 consensus: that includes Nikita Popov, Anthony Ferrara, myself and possibly Bob Weinand (can't remember if he agreed it was good or not; sorry if I'm putting words in your mouth, Bob). Github was not allowing me to post comments at the time, anyway.

The main intention of what Nikita has shared here is that it provides the baseline functionality without getting in the way of future changes or repeating past mistakes. Keep in mind this minimalism is probably required to get this included in PHP 7.0, given that we are after the feature freeze.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 5, 2015

@nikic @morrisonlevi I've updated the PR. For now I've added a specific test for self/parent to ensure they are returned as-is. Maybe we could add getReflectionClass() that returns the resolved class/interface.

@morrisonlevi
Copy link
Contributor

Just do new ReflectionClass((string) $type); no need for getReflectionClass(). That's my $0.02, anyway.

I only skimmed it, but the patch looks good.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 6, 2015

@morrisonlevi so you don't think there is a case for keeping self/parent. Well, in that case:

mbeccati@4d6d4f3

@morrisonlevi
Copy link
Contributor

I don't think we currently preserve parent and self on parameter types, do we?

Edit: It seems we do; casting a ReflectionParameter results in something like this:

Parameter #0 [ self $a ]

I will try talking to a few others tomorrow and see what everyone thinks. Shooting from the hip I think I'd prefer to not disclose self or parent at all, instead fully expanding out the type name.

@mbeccati
Copy link
Contributor Author

mbeccati commented Apr 6, 2015

@morrisonlevi TBH I wasn't even aware that self/parent were legal parameter type "hints". In any case I've added some tests to cover them as well (to both branches).

@mbeccati
Copy link
Contributor Author

@morrisonlevi any further feedback?

@nikic
Copy link
Member

nikic commented May 27, 2015

And I concur with that proposal ;) For the record, the idea behind this is as follows:

By adding something like isClassOrInterface() we're pretty much hardcoding against the current situation. If we add enums, that method name is broken. isClassLike(), while better, still has fundamentally the same issue. If we introduce some kind of type alias syntax like type iterable = array|Traversable then isClassLike() would be wrong as well. In the end the only thing we can distinguish is whether something is a special builtin type or if it isn't. Everything else we do not know statically.

The choice of isBuiltin() over isPrimitive() is that our docs have a pretty clear definition of what a primitive type is and stuff like callable or void doesn't fit in that category.

@mbeccati mbeccati force-pushed the reflection-typehints-sth-v5 branch 2 times, most recently from ed6a683 to ed498f4 Compare May 27, 2015 23:26
@mbeccati
Copy link
Contributor Author

@nikic @morrisonlevi Updated and rebased, even though it looks like github master is out-of-sync atm.

In any case, please do not merge this one, I can manually merge https://github.com/mbeccati/php-src/tree/reflection-typehints-sth-v6 which has @sgolemon 's original implementation plus a squashed commit of everything I did on top of her work.

@morrisonlevi
Copy link
Contributor

You can squash these and force-push to this branch; github will handle it properly. I doubt that anyone who has this branched checked out will care (or should be able to figure out how to get the latest).

@morrisonlevi
Copy link
Contributor

I would also recommend renaming ext/reflection/tests/parameters_typehint.phpt to ext/reflection/tests/parameters_types.phpt.

@mbeccati mbeccati force-pushed the reflection-typehints-sth-v5 branch from ed498f4 to a710695 Compare May 28, 2015 07:25
@mbeccati
Copy link
Contributor Author

@morrisonlevi sure, I didn't do it right away to make your review easier. It's squashed now and the test is called ReflectionType_001.phpt, to match the existing ones.


RETVAL_BOOL((param->fptr->type == ZEND_INTERNAL_FUNCTION ?
((zend_internal_arg_info*)param->arg_info)->type_hint :
param->arg_info->type_hint) != 0);
Copy link
Member

Choose a reason for hiding this comment

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

There is no need to differentiate between internal/userland here (and in getType), as the structures are compatible (by design). So it's okay to directly check param->arg_info->type_hint.

@mbeccati mbeccati force-pushed the reflection-typehints-sth-v5 branch from 587f440 to 0e7821c Compare May 30, 2015 07:37
@mbeccati
Copy link
Contributor Author

@nikic fixed leak and all tests are now passing. Tbh I haven't digged into the trampoline stuff too much, but

mbeccati@0e7821c#diff-39e3ad3b3135c65cc314ca09cc835530L2989

was required to properly pick the class names. However the same check is missing from most of the similar snippets in other parts of the file, so I'm wondering if I did something wrong.

@nikic
Copy link
Member

nikic commented May 30, 2015

Hm, I hadn't thought of the problem with the class name for via-handler functions. The check you added will not generally work, because the function __invoke proxies to could also be an internal function (if created via getClosure). Not sure how to fix that just now.

@nikic
Copy link
Member

nikic commented Jun 1, 2015

@dstogov What do you think about this problem? Basically the comment in http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_closures.c#210 is not true as far as Reflection is concerned. Do we need an extra flag to distinguish whether it's user or internal arginfo or do you see some other way to solve this?

@nikic
Copy link
Member

nikic commented Jun 7, 2015

As far as I'm concerned you can merge this PR -- the via-handler issue is a tangential problem we can resolve later.

@Majkl578
Copy link
Contributor

Majkl578 commented Jun 7, 2015

I think there should be a method to get the name of the type, without explicit string cast, like ReflectionType::getName(). (Or did I just miss it?)

Also ReflectionType::getClass() : ReflectionClass might be very handy. (Similar to ReflectionParameter::getClass()).

@mbeccati
Copy link
Contributor Author

mbeccati commented Jun 7, 2015

@nikic will do in the next few days.

@Majkl578 TBH, I find the requirement to use a string cast a bit weird too. But that's how the RFC worked and I wanted to deviate from it as little as possible.

@morrisonlevi
Copy link
Contributor

Just a reminder: this needs to be a minimal set of functionality for 7.0
that can be expanded and built onto later. It is intentionally small so as
to not interfere with other features such as Enums or Union types which may
not have a simple string name or ReflectionClass.

On Sun, Jun 7, 2015, 3:58 PM Matteo Beccati notifications@github.com
wrote:

@nikic https://github.com/nikic will do in the next few days.

@Majkl578 https://github.com/Majkl578 TBH, I find the requirement to
use a string cast a bit weird too. But that's how the RFC worked and I
wanted to deviate from it as little as possible.


Reply to this email directly or view it on GitHub
#1190 (comment).

sgolemon and others added 2 commits June 8, 2015 08:33
Conflicts:
	ext/reflection/php_reflection.c
	ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
@mbeccati mbeccati force-pushed the reflection-typehints-sth-v5 branch from 0e7821c to ec281fe Compare June 8, 2015 06:48
@mbeccati
Copy link
Contributor Author

mbeccati commented Jun 8, 2015

Merged in 11ece75

@mbeccati mbeccati closed this Jun 8, 2015
@php-pulls php-pulls merged commit ec281fe into php:master Jun 8, 2015
php-pulls pushed a commit that referenced this pull request Jun 8, 2015
* mbeccati/reflection-typehints-sth-v5:
  Reflection support for type hints and return types
  Merge remote-tracking branch 'pollita/reflection.typehint'

As discussed on #1190
@mbeccati mbeccati deleted the reflection-typehints-sth-v5 branch June 8, 2015 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants