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

[DependencyInjection][FrameworkBundle] Introducing container non-empty parameters #57611

Merged
merged 1 commit into from
Sep 19, 2024

Conversation

yceruto
Copy link
Member

@yceruto yceruto commented Jul 1, 2024

Q A
Branch? 7.2
Bug fix? no
New feature? yes
Deprecations? no
Issues -
License MIT

This new iteration (following up #57462, #56985 and symfony/recipes#1317) is about to improve the DX when we're dealing with optional parameters (this is the case for kernel.secret now and likely others out there) .

Nicolas regarding your comment on #57462 (comment), I tried, but after some tests I realized that the impact of deprecating the kernel.secret is huge and, in some cases, counterproductive, as we used to reference that parameter in many configurations, see https://github.com/search?q=language%3APHP+%25kernel.secret%25+&type=code&p=3, which is currently a convenient way to share a config value.

So I gave this new concept for container parameters a try. Basically, a non-empty parameter is one that must exist and cannot be empty (null, '' or []). It's evaluated when the ParameterBag::get() method is invoked.

Additionally, we can now connect the parameter with its source by passing a custom error message with details on how to proceed if it fails, thus improving the DX.

This is what we can achieve with this feature:

$container = new ContainerBuilder();

if (isset($config['secret'])) {
    $container->setParameter('app.secret', $config['secret']);
}

// NEW
$container->nonEmptyParameter('app.secret', 'Did you forget to configure the "app.secret" option?');

$container->register('security_service', 'SecurityService')
    ->setArguments([new Parameter('app.secret')])
    ->setPublic(true)
;

when the security_service is initiated/used, the app.secret parameter will be evaluated based on the non-empty conditions. If it's missing or empty, a helpful exception message will be thrown.

Before (case when it's missing):

You have requested a non-existent parameter "app.secret".

After:

You have requested a non-existent parameter "app.secret". Did you forget to configure the "app.secret" option?

This would also address our concern about third-party services depending on the kernel.secret parameter when APP_SECRET is empty (and the secrets option is disabled). In that case, even if they are not checking for empty secret value in their own, it'll fail.

@yceruto yceruto added the DX DX = Developer eXperience (anything that improves the experience of using Symfony) label Jul 1, 2024
@carsonbot carsonbot changed the title [DI][FrameworkBundle] Introducing container required parameters [DI] Introducing container required parameters Jul 1, 2024
@carsonbot carsonbot added this to the 7.2 milestone Jul 1, 2024
@carsonbot carsonbot changed the title [DI] Introducing container required parameters [DependencyInjection][FrameworkBundle] Introducing container required parameters Jul 1, 2024
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

I was wondering if we couldn't go with another API, namely a new argument to the constructor of the Parameter class?

new Parameter(string $id, string $errorOnEmpty = null)

Independently:
The name $container->requireParameter() made me think this would be checked ahead of time, not just before it's needed. In this regard, the naming is confusing to me because any parameter is required.

We could keep both approaches at once, but is it worth it?

@yceruto
Copy link
Member Author

yceruto commented Jul 1, 2024

I was wondering if we couldn't go with another API, namely a new argument to the constructor of the Parameter class?

new Parameter(string $id, string $errorOnEmpty = null)

Looks good to me. I guess this error message would override the general one since it's more specific, right?

Independently:
The name $container->requireParameter() made me think this would be checked ahead of time, not just before it's needed. In this regard, the naming is confusing to me because any parameter is required.

Yeah, I wasn’t 100% convinced about this name/method. Following your previous idea, I think that adding $errorOnEmpty to the $container->setParameter() instead would be better?

$container->setParamater('kernel.secret', $value, 'A non-empty secret is required.');

We could keep both approaches at once, but is it worth it?

I think so. I’ll give it a try and see

@yceruto yceruto marked this pull request as draft July 1, 2024 14:48
@stof
Copy link
Member

stof commented Jul 1, 2024

I would not add this in setParameter() as this would mean that each place setting the value of a parameter would have to define the proper validation (while in many cases, it could instead run the validation).

Adding this in the Parameter constructor would not help IMO. Most usages of this constructor are implicit through the %kernel.secret% syntax to reference parameters in argument values.
And this would also loose the benefit mentioned by @yceruto about automatically validating empty secrets for all third-party bundles using the parameter without any change on their side, because it would require them to opt-in for validation when referencing the parameter.

@yceruto
Copy link
Member Author

yceruto commented Jul 1, 2024

I would not add this in setParameter() as this would mean that each place setting the value of a parameter would have to define the proper validation (while in many cases, it could instead run the validation).

I was thinking of setting it to null by default (programmatically, for BC) which maintains the current behavior. In other words, the non-empty validation will only run with a custom message if it's set. This means the third (optional) argument in setParameter() will serve two purposes: enabling non-empty validation and providing a custom error message. I'm not sure if this is intuitive or clear enough, though.

Adding this in the Parameter constructor would not help IMO. Most usages of this constructor are implicit through the %kernel.secret% syntax to reference parameters in argument values.

I agree, although some PHP definitions could benefit from it as well. Anyway, I'll explore that further once the initial proposal is solid.

@stof
Copy link
Member

stof commented Jul 1, 2024

@yceruto but this setParameter method is called by file loaders when they find parameter definitions (and it is not called by FrameworkBundle). this puts the validation responsibility in the wrong place (if you ask end-user to enable validation, you could also ask them to pass a valid value instead, with the same effectiveness at catching the issue)

@yceruto
Copy link
Member Author

yceruto commented Jul 1, 2024

@stof I see your point. That brings me back to the current proposal of a separate method that enables this validation, no matter how/where the parameter is defined.

I'm open to hearing other alternative names for requireParameter()... nonEmptyParameter() ?

@yceruto yceruto changed the title [DependencyInjection][FrameworkBundle] Introducing container required parameters [DependencyInjection][FrameworkBundle] Introducing container non-empty parameters Jul 1, 2024
@yceruto yceruto force-pushed the container_required_parameters branch 5 times, most recently from 957f908 to 9ee1430 Compare July 2, 2024 04:42
@yceruto
Copy link
Member Author

yceruto commented Jul 2, 2024

I updated the proposal terminology from "require" to "non-empty". Hopefully it's clearer now.

Copy link
Member Author

@yceruto yceruto left a comment

Choose a reason for hiding this comment

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

By addressing the todo/comment below, many fixtures will be updated. Before continuing, I’d like to get confirmation that this approach is acceptable. Thanks!

@yceruto yceruto marked this pull request as ready for review July 2, 2024 05:09
@yceruto yceruto force-pushed the container_required_parameters branch 3 times, most recently from 8e02b47 to 84b3d59 Compare July 3, 2024 15:25
@yceruto
Copy link
Member Author

yceruto commented Jul 9, 2024

@symfony/mergers any objection to move forward in this direction? more changes in PhpDumper are coming...

@yceruto
Copy link
Member Author

yceruto commented Aug 4, 2024

Hey @nicolas-grekas, just a friendly ping to see if there’s anything else needed here?

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

LGTM. Minor comments only :)

@yceruto yceruto force-pushed the container_required_parameters branch 2 times, most recently from 6251ed8 to 53f737d Compare August 16, 2024 23:25
@yceruto
Copy link
Member Author

yceruto commented Aug 17, 2024

Comments addressed (Windows failures look unrelated).

@yceruto yceruto force-pushed the container_required_parameters branch from 53f737d to 22e9c87 Compare August 19, 2024 12:14
@yceruto yceruto force-pushed the container_required_parameters branch from 22e9c87 to 2a3923f Compare August 19, 2024 12:34
@yceruto
Copy link
Member Author

yceruto commented Sep 18, 2024

Just rebased

@fabpot
Copy link
Member

fabpot commented Sep 18, 2024

Are we sure we want to use empty()? The "0" string is probably a valid non-empty value, right? At least, this is how we're dealing with empty values everywhere else in the framework (routing comes to mind).

@mtarld
Copy link
Contributor

mtarld commented Sep 18, 2024

Agreed, empty is returning true for false and 0 value as well, and that can sometimes be considered as a valid parameter values.

IMHO, the valid "empty" parameters values could be:

  • ''
  • []
  • null

WDYT?

@yceruto yceruto force-pushed the container_required_parameters branch from eaf976a to 98156f7 Compare September 18, 2024 17:39
@yceruto
Copy link
Member Author

yceruto commented Sep 18, 2024

Agreed! I've updated it to consider only null, '', and [] as empty parameter values.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 18, 2024

I'd add false personally. 0 is ok but false?

@yceruto
Copy link
Member Author

yceruto commented Sep 19, 2024

After reconsidering, I don’t see false or 0 as empty values within their respective data types. For instance, a container parameter used as a toggle to enable or disable a feature would have false as a valid value in that context.

However, null (absence of value), an empty string '' (absence of chars), and an empty array [] (absence of elements). I can't reasoning the same way for bool, int or float data types.

@fabpot
Copy link
Member

fabpot commented Sep 19, 2024

Thank you @yceruto.

@fabpot fabpot merged commit ce8be29 into symfony:7.2 Sep 19, 2024
8 of 10 checks passed
@yceruto yceruto deleted the container_required_parameters branch September 19, 2024 12:48
javiereguiluz added a commit to symfony/symfony-docs that referenced this pull request Sep 30, 2024
…rameters (yceruto)

This PR was merged into the 7.2 branch.

Discussion
----------

[DependencyInjection] Documenting non-empty container parameters

PR symfony/symfony#57611
Closes #20233

Commits
-------

a78ff47 documenting non-empty parameters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DependencyInjection DX DX = Developer eXperience (anything that improves the experience of using Symfony) Feature FrameworkBundle Status: Reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants