Skip to content

Fix implode() function signature #11991

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

kamil-tekiela
Copy link
Member

@kocsismate Please review this and tell me if we can do it like this.

This function cannot take null as the second parameter. Allowing it to do so leads to a very confusing error message.

implode('', null);
// implode(): Argument #1 ($pieces) must be of type array, string given

Looking at the documentation, there is no argument called $pieces. If someone passes a string as the first argument and null as the second, it's more likely that the second argument is the wrong one. In my opinion the stub should say that the second parameter is optional and it's an empty array by default. After reading arguments, we check if pieces is null anyway. This fixes all confusing error messages to reflect reality.

FWIW, I just got this error in real life and was VERY confused. The real error was that the second argument was null when it should have been an array, but this error message didn't tell me that.

ZEND_PARSE_PARAMETERS_END();

if (pieces == NULL) {
if (arg1_array == NULL) {
zend_type_error("%s(): Argument #1 ($pieces) must be of type array, string given", get_active_function_name());
zend_type_error("%s(): Argument #1 ($array) must be of type array, string given", get_active_function_name());
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
zend_type_error("%s(): Argument #1 ($array) must be of type array, string given", get_active_function_name());
zend_type_error("%s(): Argument #2 ($array) must be of type array, string given", get_active_function_name());

Isn't it argument #2?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's an argument number 2 only when it's not an argument number 1. In this case, it is an argument number 1.

@kocsismate
Copy link
Member

First of all, the parameter name in the error message is plain wrong indeed, it uses the old name which we renamed for PHP 8.0.

implode($array, null) mimics the implode(array $array): string signature by avoiding the UNKNOWN default value. In that sense, the null default value makes sense, since the 2nd parameter basically does not exist, and in this case, the $array parameter is the 1st one. So all in all, I'd be more happy to only improve the error message, and not the default value.

P.S. To be honest, this function should have been included to my overloaded signature RFC, but somehow I didn't find it. :/

@kamil-tekiela
Copy link
Member Author

We can also do that, but my main goal here was to disallow passing null as the second parameter. This was the main point of confusion for me. Allowing implode($array, null) doesn't make sense because it's more likely that if someone passes the second argument, they want the first argument to be a string. With an empty array as the default parameter, we avoid having to use UNKNOWN.

Also, the documentation doesn't help in this case because in the documentation, it's said that the signature is implode(string $separator, array $array): string, which is not true. The second parameter is marked as non-nullable. At least my fix would make the reality match more closely to what is being documented.

Do you see any disadvantages or errors in my bug fix?

@ju1ius
Copy link
Contributor

ju1ius commented Aug 18, 2023

Hi,

Maybe the function implementation should switch on ZEND_NUM_ARGS() and act differently depending on wheter it is 1 or 2 ?

This would have the benefit to make the logic easier to follow (I find it currently confusing - as shown by @jorgsowa's comment).

switch ZEND_NUM_ARGS() {
  case 1:
    // this is the 1-argument signature,
    // argument #1 ($array) must be of type array
    break;
  case 2:
    // this is the 2-arguments signature,
    // argument #1 ($separator) must be of type string
    // argument #2 ($array) must be of type array
    break;
  default:
    // already checked by ZPP
    ZEND_UNREACHABLE();
}

@kamil-tekiela
Copy link
Member Author

I just want to add that whichever way we do it, the error message will be inaccurate. The main thing we should fix is to disallow passing null as the second param.

if (arg1_array == NULL) {
zend_type_error("%s(): Argument #1 ($array) must be of type array, string given", get_active_function_name());
RETURN_THROWS();
}
Copy link
Contributor

@ju1ius ju1ius Aug 18, 2023

Choose a reason for hiding this comment

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

Suggested change
}
}
arg1_str = ZSTR_EMPTY_ALLOC();
pieces = arg1_array;

@kamil-tekiela kamil-tekiela force-pushed the Fix-implode-error-message branch from ee0be5f to 6bc6a84 Compare August 18, 2023 14:29
@ju1ius
Copy link
Contributor

ju1ius commented Aug 18, 2023

TBH, I can't remember having ever used the 1-argument form of this function. I agree with @kocsismate that it should just be deprecated.

@bwoebi
Copy link
Member

bwoebi commented Aug 18, 2023

I disagree with deprecating it - it's a bit unfortunate that the order is $separator, $array instead of $array, $separator.
But apart from that, I'm using the 1-argument form all the time.

@kocsismate
Copy link
Member

it's a bit unfortunate that the order is $separator, $array instead of $array, $separator

What's even more unfortunate is that we deprecated the latter form... :(

The main thing we should fix is to disallow passing null as the second param.

I don't have a good idea, only a somewhat unorthodox one: let's reuse join() for the 1-parameter signature. This would mean that we should deprecate the 2-parameter version of join() + the 1-parameter version of implode(), which is a very arguable decision I know, but at least this would give us clear signatures and error messages...

Maybe the function implementation should switch on ZEND_NUM_ARGS() and act differently depending on wheter it is 1 or 2 ?

Relying on the number of parameters passed would make the default value of the 2nd parameter UNKNOWN IMO which is worse then the current situation.

@ju1ius
Copy link
Contributor

ju1ius commented Aug 20, 2023

it's a bit unfortunate that the order is $separator, $array instead of $array, $separator

What's even more unfortunate is that we deprecated the latter form... :(

Well, that's highly subjective... In functional languages for example, the collection to operate on is always passed last (because of partial application). So from my POV, it's rather fortunate that for once PHP chose the correct order instead of having it backwards like in array_filter...

But anyway, consistency and correct error messages matter more than style preferences.

Maybe the function implementation should switch on ZEND_NUM_ARGS() and act differently depending on wheter it is 1 or 2 ?

Relying on the number of parameters passed would make the default value of the 2nd parameter UNKNOWN IMO which is worse then the current situation.

Just to clarify, my comment did not concern the function signature, just how the internal logic was implemented - which was initially so confusing to follow that several people initially thought that the patch was doing something wrong WRT the argument number. Unless I'm missing something, switching on ZEND_NUM_ARGS internally does not make the default value UNKNOWN in any way.

@bukka
Copy link
Member

bukka commented Aug 24, 2023

I don't think we can disallow this in the stable version as it might potentially break some apps that are using implode($array, null) (doesn't really matter if it is confusing - we should just not introduce such BC break). I'm fine with this going to 8.3 though.

@kamil-tekiela kamil-tekiela force-pushed the Fix-implode-error-message branch from 6bc6a84 to 37c979e Compare August 24, 2023 19:51
@kamil-tekiela kamil-tekiela changed the base branch from PHP-8.1 to master August 24, 2023 19:51
@kamil-tekiela kamil-tekiela requested a review from bukka as a code owner August 24, 2023 19:51
@kamil-tekiela
Copy link
Member Author

I rebased this PR against master now. For PHP 8.1, I will only make the tiny change to the text of the error message to use $array instead of $pieces.

@kamil-tekiela kamil-tekiela force-pushed the Fix-implode-error-message branch from 37c979e to 9cea77d Compare August 24, 2023 20:23
@kamil-tekiela kamil-tekiela changed the base branch from master to PHP-8.3 August 30, 2023 22:18
@bukka bukka changed the base branch from PHP-8.3 to master September 7, 2023 12:55
@Girgias
Copy link
Member

Girgias commented Dec 1, 2023

Superceded by #12683

@Girgias Girgias closed this Dec 1, 2023
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.

7 participants