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

Allow IBootstrap::boot to have services injected directly #26723

Closed
wants to merge 2 commits into from

Conversation

ChristophWurst
Copy link
Member

@ChristophWurst ChristophWurst commented Apr 23, 2021

I might be doing something uncontroversial that is totally against my very own principals but I think I found a legit use case for duck typing with php.

Currently when you implement your \OCP\AppFramework\Bootstrap\IBootstrap::boot you are likely to need stuff from the DI container. As we all know service locators are bad, so we don't query the things but use the \OCP\AppFramework\Bootstrap\IBootContext::injectFn to indirectly call another method and some magic injects the parameters of the method/function. So far, so good, but we can do better than this.

What if you can add arbitrary services to your boot and type hint them, and Nextcloud will inject them for you? No more explicit injectFn, yay.

This is nice from an app developer perspective. The downside is that the design gets a bit impure. We are breaking the Liskov substitution principle and the application classes are not interchangeable anymore. But frankly that is not really what they are meant for, especially the boot method, so I would accept this.

Another downside is that the documentation of the method isn't as nice anymore as there is no docblock for the method but just a one-liner. That means no @since, no parameter description and so on. This sucks, but documentation can help and we can link to those docs from there.

Phpunit and psalm also seem to be okay with this.

This same concept can be reused for other similar code like occ commands (when we finally have our own API, background jobs, tasks, and so on.

Ref https://psalm.dev/r/a9d551181c some experiments with the duck typing and Psalm

Edit: forgot to mention that the idea came from https://laravel.com/docs/8.x/providers#boot-method-dependency-injection. Taking a look at their approach we might drop the boot method from the interface call it after a method_exists check on the application instance. That way apps can also omit the method if there is no need for it.

$injector = new FunctionInjector($application->getContainer(), [
IBootContext::class => $context,
]);
$injector->injectFn([$application, 'boot']);
Copy link
Member Author

Choose a reason for hiding this comment

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

reported that Psalm chokes on this at vimeo/psalm#5667

@ChristophWurst
Copy link
Member Author

@juliushaertl @miaulalala @MorrisJobke @nickvergessen @rullzer what do you think? Yay or nay?

After a few days of thinking I still consider this a step forward. The uglyness of this (duck typing) mostly happens under the hood. From a developer perspective this doesn't have any noticeable downside. Also from a performance perspective I don't really have concerns as php's reflection is very fast.

* @param IBootContext $context
*
* @since 20.0.0
*/
Copy link
Member

Choose a reason for hiding this comment

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

Should be fine to just put this in the dev manual, right? At least that is fine for me.

@MorrisJobke
Copy link
Member

@juliushaertl @miaulalala @MorrisJobke @nickvergessen @rullzer what do you think? Yay or nay?

Yay. I like it and it is a better flow from the developers perspective, because less of our specific functions and DI concepts are needed. It's just the same concept as with the constructor and I like it 👍

@rullzer
Copy link
Member

rullzer commented Apr 29, 2021

Yeah I find this acceptable as well.

@nickvergessen
Copy link
Member

So generally fine as well.

Another thought I had recently: We could also have made the boot() function return a string of public function names to call with injection on this $application object. Would keep the boot function a bit more discoverable as it's still in the interface, but not sure if it is really better codewise.

@ChristophWurst
Copy link
Member Author

Another thought I had recently: We could also have made the boot() function return a string of public function names to call with injection on this $application object. Would keep the boot function a bit more discoverable as it's still in the interface, but not sure if it is really better codewise.

Not ideal either as that calls for typos and doesn't feel idiomatic at all.

@ChristophWurst
Copy link
Member Author

Since the annotated @method doesn't quite work with static analysis and also isn't easy to document I think I'd just drop it from the interface. This is a forward-compatible change. Existing apps can still have their boot and it will work as before. The only thing one can't do is omit the boot method (would be valid 22+) but then ship the app for Nextcloud 20 or 21.

@nickvergessen
Copy link
Member

The only thing one can't do is omit the boot method (would be valid 22+) but then ship the app for Nextcloud 20 or 21.

I heard https://github.com/ChristophWurst/nextcloud_composer can help detecting that ;)

This was referenced May 20, 2021
@blizzz blizzz mentioned this pull request Jun 2, 2021
57 tasks
@skjnldsv
Copy link
Member

Status? :)

@ChristophWurst
Copy link
Member Author

Status? :)

This would/will be great but it needs a bit of work. I can finish when I get some time to do so.

@skjnldsv skjnldsv mentioned this pull request Oct 13, 2021
@skjnldsv skjnldsv modified the milestones: Nextcloud 23, Nextcloud 24 Oct 21, 2021
@ChristophWurst ChristophWurst removed this from the Nextcloud 24 milestone Dec 23, 2021
@ChristophWurst ChristophWurst added 0. Needs triage Pending check for reproducibility or if it fits our roadmap and removed 2. developing Work in progress labels Dec 23, 2021
@ChristophWurst ChristophWurst deleted the enhancement/injectible-bootstrap-boot branch December 29, 2021 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0. Needs triage Pending check for reproducibility or if it fits our roadmap enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants