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

Ensure count() is not called on a string #2888

Merged

Conversation

mattdavenport
Copy link
Contributor

This PR fixes the PHP 8.1 deprecation warning on setUploadFileId by checking the existence of preg_match values using isset() rather than count() which is now deprecated for string types.

@github-actions github-actions bot added Component: lib/Varien Relates to lib/Varien Component: lib/* Relates to lib/* labels Jan 3, 2023
@sreichel sreichel added the PHP 8.1 Related to PHP 8.1 label Jan 3, 2023
&& (count($file[0]) > 0)
&& (count($file[1]) > 0)
&& isset($file[0])
&& isset($file[1])
Copy link
Contributor

Choose a reason for hiding this comment

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

is isset() a good enoug replacement here? If they used count() on a string here, then it was probably meant for the lengts, to avoid empty strings.
Might a is_string() && strlen() > 0 be a better replacement? What other types can we expect here besides strings?

Copy link
Collaborator

@sreichel sreichel Jan 3, 2023

Choose a reason for hiding this comment

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

Might a is_string() && strlen() > 0

Shorter $file[0] !== ''?

Edit: think this is something rector would fix (if i remember right)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. It seems like preg_match can potentially return integers as a match as well according to documentation examples, although some of the docs are contradictory. In the examples there are unquoted integers as matches, but the return type description states the text that is matched.

Regarding the logic: The 0-index is simply the string to be matched (if a match is present), so checking existence on that should be sufficient. The 1-index will be a match (or not set at all), so I believe checking existence should be sufficient. Although I don't see a guarantee that the match (if present) will necessarily have a length > 0, this is my assumption. In testing I'm unable to match a 'space' character either.

Copy link
Member

Choose a reason for hiding this comment

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

count() when used on a string pre PHP 8.0, had the same effect of converting the value to an array before calling it. So count('') would still result in 1 because it's similar to count(['']).

From the context of the usage, I think the last two conditions are useless and can be removed since for this specific regex if count($file) > 0, then $file[0] and $file[1] are guaranteed to be set (empty string if they are not matched), and by changing it to !== '' we are changing the behavior.

Copy link
Collaborator

@sreichel sreichel Jan 4, 2023

Choose a reason for hiding this comment

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

If they used count() on a string here, then it was probably meant for the lengts, to avoid empty strings.

This would not work. In PHP7 count() on a string always returned int 1 (not checking for length)

Edit: count(null) returns int 0 in PHP7, so

From next lines ....

$fileAttributes = $_FILES[$file[0]];

Array key $file[0] must be string or int. Neither int nor empty string make sense here, so i think @Flyingmana is right with string comparison.

Comment on lines 507 to 509
if (count($file) > 0
&& (count($file[0]) > 0)
&& (count($file[1]) > 0)
&& isset($file[0])
&& isset($file[1])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I did some tests on

preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);`. 

$fieldId is int or null or 'abc', $file is [].

$fieldId is '1[2]', $file is

 array(3) {
  [0] => string(4) "1[2]"
  [1] => string(1) "1"
  [2] => string(1) "2"
}

$fieldId is '[abc]', $file is

 array(3) {
  [0] => string(5) "[abc]"
  [1] => string(0) ""
  [2] => string(3) "abc"
}

$fieldId is 'abc[]', $file is

 array(3) {
  [0] => string(5) "abc[]"
  [1] => string(3) "abc"
  [2] => string(0) ""
}

$fieldIs is 'abc[xyz]', $file is

 array(3) {
  [0] => string(8) "abc[xyz]"
  [1] => string(3) "abc"
  [2] => string(3) "xyz"
}

The next statement

array_shift($file);

removes the first element, now $file has the last 2 elements, both are string.

Given the above, we should look for non-empty $file[1] and $file[2], $file[0] is irrelevant as it is removed.

Suggested change
if (count($file) > 0
&& (count($file[0]) > 0)
&& (count($file[1]) > 0)
&& isset($file[0])
&& isset($file[1])
if (count($file) === 3 && $file[1] && $file[2]) {

@colinmollenhour
Copy link
Member

Why not simply this?

if (preg_match('/^(\w+)\[(\w+)\]$/', $fileId, $file)) {

If the statement is true then $file[1] and $file[2] are guaranteed to be set and non-emtpy by virtue of the regex pattern (notice I changed the regex as well). Also the regex was too permissive as the subpatterns should not match special characters so there is some small chance that allowing them could be exploited somewhere later.

Comment on lines 505 to 509
preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);

if (count($file) > 0
&& (count($file[0]) > 0)
&& (count($file[1]) > 0)
&& isset($file[0])
&& isset($file[1])
Copy link
Collaborator

Choose a reason for hiding this comment

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

@colinmollenhour suggestion is even better:

Suggested change
preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);
if (count($file) > 0
&& (count($file[0]) > 0)
&& (count($file[1]) > 0)
&& isset($file[0])
&& isset($file[1])
if (preg_match('/^(\w+)\[(\w+)\]$/', $fileId, $file)) {

Instead of checking the results of preg_match by counting the length of
the string, simply check for the success value. This prevents PHP 8.1
deprecation warnings.
@mattdavenport mattdavenport force-pushed the fix/php8.1/upload-countable-bug branch from 31f75b0 to 3ec2e39 Compare January 6, 2023 16:09
@mattdavenport
Copy link
Contributor Author

Code updated. Thanks everyone for the feedback!

@sreichel sreichel merged commit 844ec38 into OpenMage:1.9.4.x Jan 6, 2023
fballiano pushed a commit that referenced this pull request Jan 6, 2023
Instead of checking the results of preg_match by counting the length of
the string, simply check for the success value. This prevents PHP 8.1
deprecation warnings.
@fballiano
Copy link
Contributor

cherry-picked to 20.0 since there were no conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: lib/Varien Relates to lib/Varien Component: lib/* Relates to lib/* PHP 8.1 Related to PHP 8.1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants