Skip to content

Better document constructors#10127

Merged
kocsismate merged 3 commits intophp:PHP-8.2from
kocsismate:constructor-methodsynopses
Jan 1, 2023
Merged

Better document constructors#10127
kocsismate merged 3 commits intophp:PHP-8.2from
kocsismate:constructor-methodsynopses

Conversation

@kocsismate
Copy link
Copy Markdown
Member

@kocsismate kocsismate commented Dec 18, 2022

Until #10098 default constructors were sometimes documented, sometimes omitted. The above PR made adding documentation for constructor of all non-abstract classes required. However, this is not desired when such a class inherits a constructor from its parent. The current PR fixes the behavior the following way:

  • documents inherited constructors (along with destructors)
  • makes it possible to generate/replace the methodsynopsis of implicit default constructors which don't have a stub

@kocsismate kocsismate requested review from Girgias and cmb69 December 18, 2022 21:33
@kocsismate kocsismate changed the base branch from master to PHP-8.2 December 18, 2022 21:33
@kocsismate kocsismate force-pushed the constructor-methodsynopses branch from 99d4382 to 7e27c03 Compare December 18, 2022 21:35
Until php#10098 default constructors were sometimes documented, sometimes omitted. The above PR made adding documentation for constructor of all non-abstract classes required. However, this is not desired when such a class inherits a constructor from its parent. The current PR fixes the behavior the following way:
- documents inherited constructors (along with destructors)
- makes it possible to generate/replace the methodsynopsis of implicit default constructors which don't have a stub counterpart
@kocsismate kocsismate force-pushed the constructor-methodsynopses branch from 7e27c03 to e609d1d Compare December 18, 2022 21:42
@kocsismate
Copy link
Copy Markdown
Member Author

kocsismate commented Dec 18, 2022

I've just realized that reflection still not recognizes these constructors (e.g. new ReflectionMethod("ZipArchive", "__construct");) results in an exception).

I think this should be addressed in PHP 8.3 so that each class has to either declare or inherit a constructor (this would only be enforced by gen_stub.php). PHP 8.2 could still emulate the default constructors as per this PR. What do you think?

@Girgias
Copy link
Copy Markdown
Member

Girgias commented Dec 22, 2022

I've just realized that reflection still not recognizes these constructors (e.g. new ReflectionMethod("ZipArchive", "__construct");) results in an exception).

I think this should be addressed in PHP 8.3 so that each class has to either declare or inherit a constructor (this would only be enforced by gen_stub.php). PHP 8.2 could still emulate the default constructors as per this PR. What do you think?

I'm not sure, it makes sense because the main reason why a constructor is not implemented is that it doesn't take any argument, because it is pointless. And documenting it when it doesn't exist seems like a mistake in the same way as changing the internal behaviour because you can hit this exact same issue in userland: https://3v4l.org/mOTEF

@cmb69
Copy link
Copy Markdown
Member

cmb69 commented Dec 22, 2022

I've just realized that reflection still not recognizes these constructors (e.g. new ReflectionMethod("ZipArchive", "__construct");) results in an exception).

Hmm, this is an issue. If we document ::__construct(), but there is none, that is at least confusing for users. At the very least, we would need to document that these methods do not really exist, but it would probably be better to not document them. Maybe some other tool could help us to find which of the currently documented constructors don't actually exist (might be very hard wrt. PECL/extensions, though)?

@kocsismate
Copy link
Copy Markdown
Member Author

kocsismate commented Dec 25, 2022

Hmm, this is an issue. If we document ::__construct(), but there is none, that is at least confusing for users. At the very least, we would need to document that these methods do not really exist, but it would probably be better to not document them.

I disagree with the "they do not really exist" and the "it would probably be better to not document them" parts: all classes have at least the default constructor which doesn't take any arguments and which doesn't do anything (apart from instantiating the class). That's why it makes sense to document them in my opinion (even the ones which result in an exception to clarify that they shouldn't be used).

What reflection really provides information about is whether a class has (declares or inherits) a custom constructor implementation. I agree that it's debatable to change this, but any way, from a user perspective, constructors can be invoked on any classes, no matter what reflection says.

you can hit this exact same issue in userland

Yeah, I forgot to mention this in my previous reply, but the above point stays. :)

@cmb69
Copy link
Copy Markdown
Member

cmb69 commented Dec 26, 2022

all classes have at least the default constructor

I don't think this ("default constructor") is a valid model for PHP (contrary to some other languages). Instead we should distinguish between object creation and construction (i.e. calling the constructor). While the PHP manual doesn't seem to be particularly clear on this, the language specification is:

The object is then initialized by calling the class's constructor passing it the optional argument-expression-list. If the class has no constructor, the constructor that class inherits (if any) is used. The class can also specify no constructor definition, in this case the constructor call is omitted.

Ignoring reflection (or reflection like features), and while it is generally not recommended to explicitly call the constructor, that is possible, but only if a __construct() method has been defined (or inherited): https://3v4l.org/VC2n6

Of course, one may argue that there is a default constructor (if not explicitly defined or inherited) which is called, but can't be called explicitly and is not "visible" to reflection, but in my opinion, this complicates what is actually going on, and as such is not a helpful model for userland developers (it is more complex for internals due to .get_constructor() and the "ctor" naming, but I'd rather "shield" userland developers from this).

@kocsismate
Copy link
Copy Markdown
Member Author

kocsismate commented Dec 26, 2022

Ignoring reflection (or reflection like features), and while it is generally not recommended to explicitly call the constructor, that is possible, but only if a __construct() method has been defined (or inherited): https://3v4l.org/VC2n6

Thank you for highlighting this fact, I completely forgot to check it (even though I did check other magic methods...). You persuaded me that documenting non-existent constructors is a wrong approach, so I did a few followups:

Copy link
Copy Markdown
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

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

I still don't really understand how gen_stub works, so trusting you on that.

Copy link
Copy Markdown
Member

@cmb69 cmb69 left a comment

Choose a reason for hiding this comment

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

Thank you! Looks good to me (as far as I can tell).

@kocsismate kocsismate merged commit 7b08fe9 into php:PHP-8.2 Jan 1, 2023
@kocsismate kocsismate deleted the constructor-methodsynopses branch January 1, 2023 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants