Skip to content
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

feat(php): improve return typehint when repeatedfield #11734

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

bshaffer
Copy link
Contributor

@bshaffer bshaffer commented Jan 31, 2023

Adds the type of the RepeatedField to the PHPDoc, e.g.

/**
 * @return \Google\Protobuf\Internal\RepeatedField<int>
 */

Whereas before, the <int> part was not included.

This is the "getter" counterpart for #4533

// accommodate for edge case with multiple types.
size_t start_pos = type.find('|');
if (start_pos != std::string::npos) {
type.replace(start_pos, 1, ">|\\Google\\Protobuf\\Internal\\RepeatedField<");
Copy link
Member

Choose a reason for hiding this comment

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

Can we use ArrayAccess instead? RepeatedField is an internal type and we do not want to expose it to public interfaces.

Copy link
Contributor Author

@bshaffer bshaffer Feb 2, 2023

Choose a reason for hiding this comment

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

This makes sense to me as long as there are no methods of RepeatedField that we want IDEs to typehint (e.g., we want the users to only see the objects as an array, and NOT as a RepeatedField).

Also, if we do that, we should also do that for the setter typehints, for consistency. In this case, I think the best approach would be to use [], such as @return string[].

As changes like this will effect most of the files in our repo, making them at the same time would be ideal. If that sounds good to you, I'll update this PR to remove RepeatedField entirely for both getters and setters, in favor of the type[] syntax!

Copy link
Member

Choose a reason for hiding this comment

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

That sounds good to me. The only caveat is that I'm not sure if we support assignment of PHP arrays to repeated fields or not.

Copy link
Member

Choose a reason for hiding this comment

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

Ok sounds good, yes let's remove RepeatedField in getters and setters. If we use type[] we should be satisfying that by implementing ArrayAccess, yes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@haberman yes that's correct! ArrayAccess will handle anything annotated with type[]

@bshaffer
Copy link
Contributor Author

bshaffer commented Feb 14, 2023 via email

Copy link

We triage inactive PRs and issues in order to make it easier to find active work. If this PR should remain active, please add a comment.

This PR is labeled inactive because the last activity was over 90 days ago. This PR will be closed and archived after 14 additional days without activity.

@github-actions github-actions bot added the inactive Denotes the issue/PR has not seen activity in the last 90 days. label Dec 10, 2023
Copy link

We triage inactive PRs and issues in order to make it easier to find active work. If this PR should remain active or becomes active again, please reopen it.

This PR was closed and archived because there has been no new activity in the 14 days since the inactive label was added.

@github-actions github-actions bot closed this Dec 25, 2023
@bshaffer
Copy link
Contributor Author

This is kinda bizarre - so if you ignore a PR for long enough you use that as justification to close it?

@haberman
Copy link
Member

haberman commented Jan 2, 2024

The PR is kept open if there are any comments on the issue. It's to gauge whether there is still interest from the author.

I'll reopen this one.

@haberman haberman reopened this Jan 2, 2024
@github-actions github-actions bot removed the inactive Denotes the issue/PR has not seen activity in the last 90 days. label Jan 3, 2024
@haberman haberman added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Jan 30, 2024
@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Jan 30, 2024
@haberman
Copy link
Member

Hi @bshaffer , can you confirm an answer to my most recent question above?

Ok sounds good, yes let's remove RepeatedField in getters and setters. If we use type[] we should be satisfying that by implementing ArrayAccess, yes?

@haberman
Copy link
Member

It looks like the PR is still using RepeatedField. Can you update it to use type[] instead?

@bshaffer bshaffer added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label May 1, 2024
@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label May 1, 2024
@bshaffer
Copy link
Contributor Author

bshaffer commented May 1, 2024

@haberman so a few thoughts on this...

There are a few issues with typing things as we have now. This depends on the context of. the typehint:

Typehinting a setter as type[]

Protobuf Message setters accept both array and RepeatedField types. Using the type[] type-hint says that we expect this variable to be an array. This is usually okay, since normally these are passed in as a user-defined array. However, static analyzers will complain if it detects a RepeatedField being passed in (even though we accept them). I could see this happening when the getter from one message is used as a setter for another. For example:

$a = $message1->getA();
$message2->setA($a);

This would not be a problem if we typehint the return types as type[] as well, but that is problematic in its own way (see below).

Type-hinting return types as type[]

If we use type[] for return type-hints, instead of RepeatedField<type>, we are telling our users/static analyzers/IDEs that these things need to be arrays. This may cause issues with static analyzers and IDEs. The main issue is that there are methods which work for array types which are not available to RepeatedField. For instance, array_merge and the + operator do not work on RepeatedField objects. The static analyzers will miss them, and a user may try to use them and be unpleasantly surprised to find they're not actually arrays.

Additionally, because the static analyzer expects an array but gets a RepeatedField, it may send out an error. In my testing, this doesn't actually happen in Protobuf because the static analyzer doesn't know what's being returned. But potentially someday it may throw an error here.

So our choices are as follows

  1. Set return type-hints as type[] and hope nobody minds. I personally think this is a bad way to go, as users may be surprised to find that objects which they thought were arrays are in fact RepeatedField
  2. Set return type-hints to RepeatedField<type>. One thing we can do is shorten this to not contain the namespace, since we know that in every case where we use RepeatedField, we import it at the top of the class. This would at least make the typing less verbose. The problem here is in the case above, this would throw a static analysis error for setters because they expect type[].
  3. Use Traversable<type> instead. This takes advantage of the Traversable interface in PHP, and is essentially the opposite of using the type[] typehint, in the sense that we are typing it stricter than it actually is. We are saying it can be used for foreach, but nothing else. This means in some cases, things like $value = $repeatedField[0] would throw a static analysis error. Since that usage is probably discouraged anyway, maybe this isn't a problem. But it would definitely be an issue for MapField types (which is out of scope for this PR). But again, this would throw a static analysis error for setters because they expect type[].

Conclusion

  • If we want to be 100% accurate, we should type parameters as type[]|RepeatedField<type> and returns as RepeatedField<type>.
  • If we want to hide the RepeatedField type (and we think that the potential issues with static analyzers and IDEs are negligible, which is probably true), we should type parameters as type[] and returns as Traversable<type>

@haberman
Copy link
Member

haberman commented May 1, 2024

What about ArrayAccess<T>? That seems like the most accurate type to return.

@bshaffer
Copy link
Contributor Author

bshaffer commented May 1, 2024

What about ArrayAccess<T>? That seems like the most accurate type to return.

ArrayAccess is similar to marking as Traversable in the sense that it's a more narrowly scoped type. It would still encounter the issues I mentioned for Traversable. IMHO Traversable is better because I imagine the normal usage of RepeatedField is to traverse it (e.g. in foreach) or call iterator_to_array to make it into an array. Neither of these are possible with ArrayAccess (and would result in a static analysis error). I think ArrayAccess is better suited for MapField types.

@bshaffer
Copy link
Contributor Author

bshaffer commented Jun 6, 2024

Another thought on this is we could typehint the returns as ArrayAccess<T>&Traversable<T> using the Intersection Types in PHP 8.1. This mirrors the actual RepeatedField implementation, which implements ArrayAccess, IteratorAggregate (which extends Traversable), and Countable.

@bshaffer
Copy link
Contributor Author

bshaffer commented Jun 6, 2024

Heads up @haberman , we are receiving a few issues (#14666, #15673) related to this. I think we should make a decision on how to support them!

@jontro
Copy link

jontro commented Jul 3, 2024

I would also like to add that UnaryCall, ServerStreamingCall, ClientStreamingCall and BidiStreamingCall would be nice to type this way too.

@bshaffer
Copy link
Contributor Author

@jontro can you describe a little bit more what you're asking for? This repo is for protobuf, you may be looking for the gRPC repo, where those classes are defined, or the google cloud PHP repo, where the APIs are defined.

If you could provide an example, that would be very helpful.

@jontro
Copy link

jontro commented Sep 15, 2024

@bshaffer Sorry for the confusion. Found a corresponding issue in the grpc repo for this in case anyone else is looking grpc/grpc#33431

@JasonLunn JasonLunn added the php label Oct 11, 2024
@roxblnfk
Copy link

roxblnfk commented Nov 22, 2024

RepeatedField is an internal type and we do not want to expose it to public interfaces.

@haberman But RepeatedField is already exposed in all public interfaces, isn't it? Let's start by slightly improving what already exists: add @template to RepeatedField with using it in all return types.

We can consider replacing RepeatedField with \ArrayAccess<int, Value>|\Traversable<int, Value>|\Countable separately.

@bshaffer bshaffer added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Nov 22, 2024
@bshaffer
Copy link
Contributor Author

bshaffer commented Nov 22, 2024

@roxblnfk we've decided to move RepeatedField up one level out of the Internal namespace, and then use the templating typehints like you've described. I've updated this PR, so hopefully this will be merged soon!

@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Nov 22, 2024
@bshaffer bshaffer added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Nov 28, 2024
@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Nov 28, 2024
@bshaffer bshaffer force-pushed the add-return-typehint branch from 30be71e to 8f518e0 Compare December 27, 2024 22:28
@bshaffer bshaffer added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Dec 27, 2024
@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Dec 27, 2024
@bshaffer
Copy link
Contributor Author

aaa08d8 seems to have broken all the tests.

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.

7 participants