Skip to content

[Runtime] make GenericRuntime ... generic #40513

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

Merged
merged 1 commit into from
Mar 19, 2021

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Mar 18, 2021

Q A
Branch? 5.x
Bug fix? no
New feature? yes
Deprecations? no
Tickets -
License MIT
Doc PR -

This PR will allow #40436 to move to https://github.com/symfony/psr-http-message-bridge
For the record, it builds on a prototype I wrote almost one year ago at https://github.com/tchwork/bootstrapper.

This PR makes the GenericRuntime implementation able to auto-discover runtime implementations for specific types.

It uses the autoloader for the discovery: when a closure-app requires or returns a type Vendor\Foo, it will use a convention and check if the class Symfony\Runtime\Vendor\FooRuntime exists. If yes, it will use it to resolve the corresponding type. Such runtime classes have to extend GenericRuntime so that they can use the protected API it provides. This requirement is aligned with the fact that the very convention proposed here is an implementation detail that works when using a GenericRuntime as the main runtime (This behavior can be overridden by providing explicit entries in the new runtimes option when booting the GenericRuntime.)

SymfonyRuntime can be used as both the main runtime or a type-specific runtime:

  • when used as the main runtime, it configures the typical global-state for Symfony and has a fast codepath for Symfony types, while still being generic.
  • it can also be used in another runtime as a way to resolve Symfony types (would typically be useful to [Runtime] Add support for PSR-7, PSR-15 and PSR-17 #40436 for running Console apps in a PSR-based web app.)

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

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

This PR makes the GenericRuntime implementation able to auto-discover runtime implementations for specific types.

That is only true if the runtime is located in a class starting with Symfony\Runtime\.. So if my acme/library wants to create a "auto discoverable" runtime, then I need to define that namespace.


There is no clear way how to use GenericRuntime as "main" and be able to pass options to the PsrRuntime.

@nicolas-grekas nicolas-grekas force-pushed the runtime-generic branch 5 times, most recently from 2698571 to 388c45d Compare March 18, 2021 21:21
@Nyholm
Copy link
Member

Nyholm commented Mar 18, 2021

There is no clear way how to use GenericRuntime as "main" and be able to pass options to the PsrRuntime.

Oh, I can pass the "PSR options" to the "GenericRuntime". Then on PsrRuntime::register(), I'll just take the GenericRuntime::$options and create a new instance of PsrRuntime.

Copy link
Member

@Nyholm Nyholm 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. It took me a good 2 hours to test and review. Im happy with this PR after my last comment is resolved.

Well done 👍

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 18, 2021

That is only true if the runtime is located in a class starting with Symfony\Runtime.. So if my acme/library wants to create a "auto discoverable" runtime, then I need to define that namespace.

Correct, that's the convention. This effectively means that's we're globally reserving the Symfony\Runtime namespace for this purpose. I think that's appropriate.

Adding the namespace should be done in the composer.json, as this PR does. Eg:

"psr4": {
	"Symfony\\Runtime\\Acme\\Lib\\": "src/Runtime/"
}

@nicolas-grekas nicolas-grekas force-pushed the runtime-generic branch 8 times, most recently from 0f7ff04 to 94d121d Compare March 19, 2021 10:28
@nicolas-grekas
Copy link
Member Author

(PR is ready, now tested)

@@ -25,7 +25,7 @@ interface RuntimeInterface
*
* The callable itself should return an object that represents the application to pass to the getRunner() method.
*/
public function getResolver(callable $callable): ResolverInterface;
public function getResolver(callable $callable, \ReflectionFunction $reflector = null): ResolverInterface;
Copy link
Member Author

@nicolas-grekas nicolas-grekas Mar 19, 2021

Choose a reason for hiding this comment

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

RuntimeInterface::getResolver() now takes a second \ReflectionFunction $reflector = null argument. This allows injecting the reflector to use to analyse the $callable, providing both a small perf benefit when decorating, and allowing to decouple from the native implementation when needed.

Copy link
Member

Choose a reason for hiding this comment

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

Could you show where you are using the second argument?

Copy link
Member Author

@nicolas-grekas nicolas-grekas Mar 19, 2021

Choose a reason for hiding this comment

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

I'm not, this is just applying the DI principle. Actually, I played with the idea a bit a few times already. Some applications I won't submit for now:

  • read a PHP8 attribute on the $callable to select the app-runtime;
  • decorate GenericRuntime::getResolver() in a child class;
  • build the ReflectionFunction instance out of static data (aka caching).

All those use cases need to create a ReflectionFunction instance before calling the method. It would be a shame if all decorators in the chain would have to create this again and again. Not to mention the last use case, which needs an actual injection point.

@Nyholm
Copy link
Member

Nyholm commented Mar 19, 2021

Thank you Nicolas.

@Nyholm Nyholm merged commit 6c0786b into symfony:5.x Mar 19, 2021
@nicolas-grekas nicolas-grekas deleted the runtime-generic branch March 19, 2021 15:58
@fabpot fabpot mentioned this pull request Apr 18, 2021
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