Skip to content

[HttpFoundation] Add method getString to ParameterBag #44787

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
wants to merge 1 commit into from

Conversation

SHTIKOV
Copy link

@SHTIKOV SHTIKOV commented Dec 24, 2021

Q A
Branch? 6.2
Bug fix? no
New feature? yes
Deprecations? no
Tickets Fix #...
License MIT
Doc PR symfony/symfony-docs#...

Added method getString to ParameterBag for use strict type. We really need this method to make it easier to work with strong typing, because the get method returns several data types, and we need a specific one.

@derrabus
Copy link
Member

Please rebase to 6.1. 5.4 is closed for features.

@derrabus derrabus modified the milestones: 5.4, 6.1 Dec 24, 2021
@SHTIKOV SHTIKOV changed the base branch from 5.4 to 6.1 December 24, 2021 09:19
@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 24, 2021

Please rebase to 6.1. 5.4 is closed for features.

done

Copy link
Member

@derrabus derrabus left a comment

Choose a reason for hiding this comment

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

This method might look trivial, but having a method that guarantees to return strings will improve the DX in a Symfony project with static analyzers a lot.

@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 26, 2021

This method might look trivial, but having a method that guarantees to return strings will improve the DX in a Symfony project with static analyzers a lot.

yes thanks, that's why I created this PR!

@ro0NL
Copy link
Contributor

ro0NL commented Dec 26, 2021

i'd prefer casting in user-land

@94noni
Copy link
Contributor

94noni commented Dec 26, 2021

Reminds me #39110
Do your mind change on my related PR @derrabus ? Thank you

@carsonbot carsonbot changed the title Add method getString to ParameterBag [HttpFoundation] Add method getString to ParameterBag Dec 26, 2021
@ghost
Copy link

ghost commented Dec 26, 2021

This will trigger a PHP warning (notice in 7.4) and return "Array" string if parameter is an array. I understand developers should use this method only on validated requests, right? Otherwise an application will process this string as valid even though a user sent invalid data.

However this class has other shortcut methods which do not concern this case (e.g. getInt()), so...

@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 26, 2021

This will trigger a PHP warning (notice in 7.4) and return "Array" string if parameter is an array. I understand developers should use this method only on validated requests, right? Otherwise an application will process this string as valid even though a user sent invalid data.

However this class has other shortcut methods which do not concern this case (e.g. getInt()), so...

The getString method is used when a string is expected. The getInt method will also contain incorrect array conversion logic and this warning will additionally inform the developer that he is using the ParameterBag incorrectly. In addition, if we talk about HttpFoundation, then we will never receive an array in the request.

@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 27, 2021

Reminds me #39110 Do your mind change on my related PR @derrabus ? Thank you

May be you need use ParameterBag for options and arguments in Symfony\Component\Console\Input\Input::class?

@ghost
Copy link

ghost commented Dec 27, 2021

In addition, if we talk about HttpFoundation, then we will never receive an array in the request.

Actually we will. This is the case when sending a field multiple times, like field[]=value&field[]=otherValue

@derrabus
Copy link
Member

Reminds me #39110 Do your mind change on my related PR @derrabus ? Thank you

I haven't actually. I realize that the situation appears similar to you, but let's keep the discussion about the Console componet in its dedicated tickets.

@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 27, 2021

In addition, if we talk about HttpFoundation, then we will never receive an array in the request.

Actually we will. This is the case when sending a field multiple times, like field[]=value&field[]=otherValue

oh, yes

@ro0NL
Copy link
Contributor

ro0NL commented Dec 28, 2021

we could deprecate getInt() still :}

maybe put the methods on InputBag, where it's defacto safe to cast.

@SHTIKOV
Copy link
Author

SHTIKOV commented Dec 28, 2021

We currently have a discrepancy as getAlnum, getDigits, and getAlpha removes the non-valid characters, whereas getInt converts the value to an integer.

I would love to fix this inconsistency before adding getString.

I suggest that instead of letting PHP fail when a non-valid value is passed, like an array, we return an empty string for getString and probably 0 for getInt. I would also suggest to name it differently (like convertString?) to differentiate the two behaviors. That would mean renaming getInt as well (with a BC layer).

WDYT?

Nice suggestion, I added changes, please see my solution.

{
$output = $this->get($key, $default);

return \is_array($output) ? $default : (string) $output;
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like there's been some discussion around this already so apologies if this has been covered and I missed it.

Personally I'd find the behaviour of returning the default if the value is an array surprising. While it could be argued any usage should be covered by an applications tests, I'd prefer to see exceptions on any conditions not specified by the caller.

This would also apply to defaulting the value to 0 in convertInt, and '' here, there doesn't seem to be a way to fail when an expected parameter is missing.

@ghost
Copy link

ghost commented Dec 28, 2021

I think ParameterBag's helper methods like getInt, getString, getBoolean should not modify the returned parameter value but only change the type. If the type cannot be changed without changing the value then it should throw. In other words, a value '1' can be returned from getInt() as 1, but an array should not be converted to int. Similarly the floats: '1.1' != 1, I have put a parameter xyz with a value '1.1' to the bag and when I retrieve this parameter I receive other value. Let's think about this in terms of loose comparison: if $beforeValue != $afterValue then it should not be a valid casting.
In case of arrays a valid conversion would be ['1', '2', '3'] to [1, 2, 3] but not to int/string or if it removes any element.

PS. changing method names to "convert" prefix would be a little bit helpful but imo very dangerous because the consequences might be missed very easily. If you go this way I would add a warning in the doc blocks that this methods are not only for changing types but also might change the value.
PS2. I've just noticed that getBoolean might return a falsy false, because filter_var returns false on error.

@SHTIKOV
Copy link
Author

SHTIKOV commented Jan 11, 2022

@fabpot WDYT?

@nicolas-grekas nicolas-grekas changed the base branch from 6.1 to 6.2 July 28, 2022 17:04
@nicolas-grekas
Copy link
Member

I just squashed+rebased the PR to target 6.2, please fetch and reset --hard before working again on the PR if you're still up to it. Please always rebase (never merge) when syncing with other branches.

If we go this way, I think we need to address the previous comments. Eg when an array is encountered, we should behave as InputBag does (aka throw). About the name of the new methods, what about asInt or toInt? Note that I would also suggest to rename all methods, eg getBoolean, etc.

@GromNaN
Copy link
Member

GromNaN commented Jul 28, 2022

About the name of the new methods, what about asInt or toInt? Note that I would also suggest to rename all methods, eg getBoolean, etc.

I would not invent a new name. getInt is consistent with the main method get and we already used that convention.

@VincentLanglet
Copy link
Contributor

What's the status of this PR @nicolas-grekas ?
I'm really interested by this PR so I can finish the work if needed.

@GromNaN
Copy link
Member

GromNaN commented Oct 7, 2022

We have open questions on the new method names and error handling.
The method getInt having an inconsistent and problematic behavior, we cannot change this behavior while preserving backward compatibility.

InputBag throws BadRequestException in case of invalid data, while ParameterBag relies on TypeError.

Proposition:

  • Deprecate getInt
  • Add getInteger (rename convertInt in this PR). It is stricter on input data than (int)getDigits.
  • Add getString
  • Throw a domain exception in case of invalid data in getAlnum, getAlpha, getDigits, getBoolean, getInteger, getString. (ie filter returning false). BadRequestException for InputBag and LogicException for the generic ParameterBag.

@Growiel
Copy link

Growiel commented Nov 4, 2022

My take on this entire thing is that we should validate rather than convert any of the inputs.

If I use getInt() is because I expect an int, if I don't get one, I don't want Symfony to guess one, but to tell me it wasn't a string.

// let's pretend that var is "2"
$request->query->getString('var', "5"); // returns "2"
$request->query->getInt('var', 5); // returns 2

// now let's pretend that var is "two"
$request->query->getString('var', "5"); // returns "two"
$request->query->getInt('var', 5); // returns 5 because "two" doesn't pass a ctype_digit() check

and so on for the other methods, validate and return the expected type if it matches, otherwise return the default value.

Additional getters could be considered, like getNumericString() for those working with bc_* functions. It would retourn only if the string is_numeric.

I'm open to working on this as I feel it's one of the biggest confusing, and, in the case of getInt(), sometimes dangerous behavior in the parameter bags.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Dec 15, 2022

Continued in #48548 #48525
Thank you @SHTIKOV for pushing this forward!

@Seldaek
Copy link
Member

Seldaek commented Dec 15, 2022

I think you mean #48525

fabpot added a commit that referenced this pull request Mar 19, 2023
…eprecate accepting invalid values (GromNaN)

This PR was merged into the 6.3 branch.

Discussion
----------

[HttpFoundation] Add `ParameterBag::getString()` and deprecate accepting invalid values

| Q             | A
| ------------- | ---
| Branch?       | 6.3
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | yes
| Tickets       | Fix #44787 #48219
| License       | MIT
| Doc PR        | symfony/symfony-docs#...

There were a lot of discussions on #44787 regarding the implementation. The main debate is to determine the right behavior when the value is invalid: try to convert the value, throw an exception, return the default value or the falsy?

I added plenty of test cases for the methods `getAlpha`, `getAlnum`, `getDigits`, `getInt`, ~`getInteger`~, `getString` so that we can discuss the expected result for each input value.

My goals:
- Provide safe methods to get values in the expected type. Example: The parameters being generally of type `string`, the `getString` method is useful to be sure we don't get an array.
- Reduce deprecations on methods that exist since Symfony 2.0, in one of the most popular Symfony component.

How are these getter methods used?
- Most of the time, parameter values are `string` (from routing, query string, request payload). `get` is used but does not validate the return type.
- `getInt` is used for [pagination (UX Autocomplete)](https://github.com/symfony/ux-autocomplete/blob/697f1cb4914480b811d978efe031a6f4a0dc3814/src/Controller/EntityAutocompleteController.php#L41) or [getting an index (EasyAdmin)](https://github.com/EasyCorp/EasyAdminBundle/blob/f210bd1e494b699dec54d2ef29302db1211267b5/src/Context/AdminContext.php#L157-L158)
- I think there was an unidentified breaking change when we introduced return types [#42507](https://github.com/symfony/symfony/pull/42507/files#diff-04f3b8ea71029f48853b804129631aeb9bf3dad7a7a398feb9568bf5397d0e69L117). Methods `getAlpha`, `getAlnum` and `getDigits` could return an array, but their return type have been modified to `string`, resulting in a `TypeError`. [example of use](https://github.com/asaaa1997/Licencjat/blob/8c10abaf87120c904557977ae14284c7d443c9a7/src/Controller/QuizController.php#L164)

Commits
-------

172f0a7 [HttpFoundation] Add `ParameterBag::getString()` and deprecate accepting invalid values
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.