Skip to content

[Routing] Allow defining and reusing subroutines #26529

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

Closed

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Mar 14, 2018

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #26524
License MIT
Doc PR -

Tested locally:

  • config/routes/annotations.yaml:
_subroutines:
    yyyy_mm_dd: \d{4}-\d{2}-\d{2}
  • src/Controller/HelloController.php
class HelloController extends Controller
{
    /**
     * @Route("/hello/{date<(?&yyyy_mm_dd)>}", name="hello")
     */

It works well :)

Here are the remaining steps:

  • agree on vocabulary
  • add tests
  • implement in XmlFileLoader
  • implement in RoutingConfigurator
  • handle in UrlMatcher
  • add docblocks

@@ -78,6 +78,12 @@ public function load($file, $type = null)
}

foreach ($parsedConfig as $name => $config) {
if ('_subroutines' === $name && \is_array($config) && !isset($config['resource']) && !isset($config['path'])) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why check against for resource and path ?

Copy link
Member Author

Choose a reason for hiding this comment

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

for BC

Copy link
Member

Choose a reason for hiding this comment

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

Should we trigger a deprecation if somebody tries to use _subroutines as a route name here?

@symfony symfony deleted a comment from Taluu Mar 14, 2018
@nicolas-grekas nicolas-grekas force-pushed the route-subroutines branch 2 times, most recently from 998885f to 6b68fd8 Compare March 14, 2018 20:56
@nicolas-grekas
Copy link
Member Author

PR is tested and ready. We just need to agree on the vocabulary.

@Tobion
Copy link
Contributor

Tobion commented Mar 14, 2018

we already support di parameters for route requirements, don't we? that already solves the same problem from what I can see.

@nicolas-grekas
Copy link
Member Author

we already support di parameters for route requirements, don't we? that already solves the same problem from what I can see.

@iltar @Taluu et al, this question is for you.

@Taluu
Copy link
Contributor

Taluu commented Mar 15, 2018

IMO this not really the same thing. I think it is better to have it in the routing component, while using DI parameters can be useful in some points (including in these "subroutines"). Maybe even deprecating using di parameters could be the way to go ?

@linaori
Copy link
Contributor

linaori commented Mar 15, 2018

DI parameters are not really a good extension point for this imo.

This could work in theory, but I rather not refer to DI parameters at all.

_subroutines:
    yyyy_mm_dd: '%parameters.yyyy_mm_dd%'

@javiereguiluz
Copy link
Member

javiereguiluz commented Mar 15, 2018

I love @Tobion proposal! You can use something that is already implemented, it works and you already know ... instead of inventing a very similar feature, implement it, document it, learn it, etc.

In practice, the differences would be minimal:

This already works

# config/services.yaml
parameters:
    routing.uuid: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
@Route("/foo/{bar}/", "requirements": {"bar": "%routing.uuid%"})

This would need to be implemented

# config/packages/routing.yaml
routing:
    _subroutines:
        uuid: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
@Route("/foo/{bar<(?&uuid)>}/")

@linaori
Copy link
Contributor

linaori commented Mar 15, 2018

@javiereguiluz if I have a re-usable bundle that provides a regex for UUID, I'd have to depend on a DI parameter in my controller, which is something i try to avoid, hence I'm proposing a more robust extension point with nicer DX for this.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 15, 2018

The nice thing about subroutines is that they fit the routing domain, whereas parameters are a layer on top of routing. The very fact that the #26524 RFC has been open is a signal that using parameters for this is not natural. Parameters are global things, subroutines are domain-local ones. Looking at the implementation, this fits nicely and improves the component, standalone.

@Tobion
Copy link
Contributor

Tobion commented Mar 15, 2018

IMO we should allow either or. So when this feature is really necessary, using parameters should get deprecated. There is no point in having two conceptually different solutions for the same thing.

@Tobion
Copy link
Contributor

Tobion commented Mar 15, 2018

But then you still have no replacement for di parameters in other parts of the route definition like the path itself. So this solution here looks inferior.

if I have a re-usable bundle that provides a regex for UUID, I'd have to depend on a DI parameter in my controller, which is something i try to avoid, hence I'm proposing a more robust extension point with nicer DX for this.

If you create a symfony bundle, you rely on the DI already. So there is no difference if you use a parameter or not. Also this does not mean you need to "expose" the parameter at all. You can add semantic config for this that sets the parameter internally. So this argument does not make sense to me.
The DX is up to you and has nothing to do with di paramters.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 15, 2018

Well, there is a difference between them:

  • a parameter is here to configure a value from somewhere else (semantic config/DI ext.)
  • a subroutine is like a "function call": a proper factorization unit

Yes, you can use parameters for "factorization", but that's a hack IMHO (and also why nobody used them like that apparently.)

@nicolas-grekas
Copy link
Member Author

Re-reading this, I'm still thinking this is a useful addition to the component, especially when it is used standalone. Let's make the best router out there. 4.1 already made it great.

@Tobion
Copy link
Contributor

Tobion commented Mar 16, 2018

I don't agree. There are so many solutions to this already outside the routing component that the routing component does not need to deal with this. Apart from DI parameters, people could also use YAML named references.

@sstok
Copy link
Contributor

sstok commented Mar 16, 2018

Apart from DI parameters, people could also use YAML named references.

These don't work across files do they? The great thing about attributes is that you can define them and use them anywhere. With subroutines, you can defined them at the root and reuse them in imported routes (yes this is fragile, but also allows for some flexibility).

Honestly, I completely forgot you can use parameters in a requirement/default 🤦‍♂️
And I even used it a longtime ago! 🤷‍♂️

@derrabus
Copy link
Member

Could a bundle define a subroutine that the application could use for its routing?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 16, 2018

@derrabus yes (and also for parameters as you know)

@ostrolucky
Copy link
Contributor

ostrolucky commented Mar 16, 2018

I would rather see generic solution for reusable parameters on YAML parser level. Would be nice to do parameters: thing in any YAML and have those scoped just to that one file and its imports, without component complaining about unsupported key and without dependency on DI.

Problem with DI parameters are:

  • Dependency on DI
  • Scoping issue: You need to prefix such parameters to guarantee they don't conflict with other parameters
  • Context issue: They are defined totally out of context of routing/other yaml file, so you want them prefixed even if you know that name is not going to be used somewhere else, just to signify they are for routing

@Taluu
Copy link
Contributor

Taluu commented Mar 16, 2018 via email

@ostrolucky
Copy link
Contributor

ostrolucky commented Mar 16, 2018

Other formats already have those, don't they? In PHP you can define variables, in XML you can also define common values, that's how billion laughs attack happened. Only loaders need to be changed to propagate these to children resources.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 19, 2018

There is a technical difference with the "loader-time" alternatives: PCRE limitations. By making PCRE aware of factorarization as allowed by subroutines, we create smaller regexp. Could be significant if all your URLs contain e.g. a uuid. Subroutines allow better scalability.

@linaori
Copy link
Contributor

linaori commented Mar 19, 2018

@nicolas-grekas would that mean that if you have \d+ as regex in a lot of URLs and you refer to that as a subroutine named one_or_more_digits, it would perform better?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 19, 2018

@iltar I don't think it would help for simple requirements, but for more complex ones, yes.

@nicolas-grekas
Copy link
Member Author

I'm closing here as it looks like the existing alternatives are good enough. Can be reopened later of course.

@nicolas-grekas nicolas-grekas deleted the route-subroutines branch March 19, 2018 14:30
@patrickbussmann
Copy link

Now there is no alternative in Symfony5, or?
@nicolas-grekas

@nicolas-grekas
Copy link
Member Author

Using parameters is the alternative.

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.

10 participants