Hi,
As it is explained in this ticket https://bugs.php.net/bug.php?id=81417 we use to check if a property exists by accessing it directly like we do in JavaScript.
Personally, I subscribe to this coding style and we use it all over our codebase (more than 130 000 lines of PHP code). When it became a notice, we disabled them, but now that it is a warning, it is a lot more problematic for us… We can’t even use the last version of PHP Debug for VSCode.
As I’m not ready to spend weeks upgrading our codebase because our coding style is not supported by the community, I’m looking at alternatives.
The more obvious and simple for me would be to add a setting in php.ini to allow this. So I’d like to know if it is something you would support.
If not, I guess I will have to fork PHP, but this will complicate a lot our deployment process and make our code not portable. As I believe we are not the same in this case, I hope you will support the addition of a setting…
Thanks in advance for considering this.
Best regards,
Nicolas
Hi Nicolas,
as far as I understand, adding new ini settings is not the way to go as
this would mean that your code behaves differently on different
environments.
Two suggestions:
-
Write your own error handler that ignores those errors.
-
Try some tool like Rector to rewrite your code.
Best regards
Thomas
Am 15.02.22 um 13:31 schrieb Nicolas BADIA:
Hi,
As it is explained in this ticket https://bugs.php.net/bug.php?id=81417 we use to check if a property exists by accessing it directly like we do in JavaScript.
Personally, I subscribe to this coding style and we use it all over our codebase (more than 130 000 lines of PHP code). When it became a notice, we disabled them, but now that it is a warning, it is a lot more problematic for us… We can’t even use the last version of PHP Debug for VSCode.
As I’m not ready to spend weeks upgrading our codebase because our coding style is not supported by the community, I’m looking at alternatives.
The more obvious and simple for me would be to add a setting in php.ini to allow this. So I’d like to know if it is something you would support.
If not, I guess I will have to fork PHP, but this will complicate a lot our deployment process and make our code not portable. As I believe we are not the same in this case, I hope you will support the addition of a setting…
Thanks in advance for considering this.
Best regards,
Nicolas
Le 15/02/2022 à 13:46, Thomas Nunninger a écrit :
Hi Nicolas,
as far as I understand, adding new ini settings is not the way to go
as this would mean that your code behaves differently on different
environments.Two suggestions:
Write your own error handler that ignores those errors.
Try some tool like Rector to rewrite your code.
Best regards
Thomas
Hello,
I do agree with Thomas here, changing PHP internal behavior by
configuration is a dangerous thing to do.
If I understand correctly, there is "more than 130 000 lines of PHP
code" to fix, believe me I migrated a few projects along the way from
PHP 5.6 to 8.0 over time (one was about this size, but it stopped at PHP
7.0 a few years back). They probably contain less "legacy code" since we
have strict conventions from the beginning, but we did migrate some
projects still.
Fact is, a legacy project cannot run without evolving if you want to
support a recent PHP version. The language itself evolves, and that's
for the best. Now for really legacy unmaintained projects, there's still
the solution to keep them running on deprecated PHP version using some
container technology. It's not ideal, but it can help until you finally
find the necessary resources to port.
Using a good static analysis tool and possibly some other tools such as
Rector, 130k lines is not an impossible goal.
I don't think there's an easy solution here, it's either don't upgrade,
or either refactor, but modern tooling should help you a lot with that.
If it can help you, can can still replace your array access using $a = $foo['bar'] ?? null
instead of $a = \array_key_exists('bar', $foo) ? $foo['bar'] : null
, it's both easier to read and write (you just store
?? null
in your clip board and hit paste on every use case you find).
Hope it helps,
Regards,
--
Pierre
As it is explained in this ticket https://bugs.php.net/bug.php?id=81417 we use to check if a property exists by accessing it directly like we do in JavaScript.
Personally, I subscribe to this coding style and we use it all over our codebase (more than 130 000 lines of PHP code). When it became a notice, we disabled them, but now that it is a warning, it is a lot more problematic for us… We can’t even use the last version of PHP Debug for VSCode.
The problem with your way of writing code is that it is ambiguous in
meaning, which is why this is a warning. You are not checking if a key
in an array exists, you are checking if an array value is "false-y". If
the value is an empty string, it is also interpreted as false, if it is
null, it is also interpreted as false, if it is an integer of value zero
it is also interpreted as false, if it is the string "0" it is also
interpreted as false.
When you write code as you do, it is easy to introduce subtle errors
because of these implicit casts. If you want to check if an array key is
defined, you should do it explicitly - this is not about a "coding
style", this is about writing the code in a way that actually does what
you intend it to do without it meaning multiple things of which some are
probably not expected. If you just want a quick fix without solving the
underlying problem, you can just add "?? null" to each array key access.
If you want to upgrade/improve your code base, which is a worthwhile
task, I would suggest the use of a static analyzer (like Psalm or
PHPStan) to find out where your code is ambigious. I have found many
undetected errors that way, where implicit casts have lead to unexpected
outcomes, which is why I am very much in favor of these warnings for
undefined array keys.
The problem with your way of writing code is that it is ambiguous in
meaning, which is why this is a warning.
I think that's a good way of looking at it. There's actually quite a lot
of code hiding a check like "if ($array['key'])"; roughly speaking, it's
short-hand for:
if ( array_key_exists('key', $array) && $array['key'] !== null &&
$array['key'] !== false && $array['key'] !== 0 && $array['key'] !== 0.0
&& $array['key'] !== '' && $array['key'] !== [] )
Now, if that's what you intended, there's a syntax that's slightly more
explicit while still being reasonably short: the empty() pseudo-function:
if ( ! empty($array['key']) )
On the other hand, if what you actually meant was this:
if ( array_key_exists('key', $array) && $array['key'] !== null )
Then you might have some bugs lurking, and actually want the isset()
pseudo-function instead:
if ( isset($array['key']) )
For other cases, you do have to spend a few more characters to be
explicit, often using the "??" operator; for instance, if you expect
$array['key'] to be either unset or a boolean, and want this:
if ( array_key_exists('key', $array) && $array['key'] === true )
Then the shortest is probably something like this:
if ( $array['key'] ?? false === true )
Regards,
--
Rowan Tommins
[IMSoP]
On Tue, Feb 15, 2022 at 3:07 PM Rowan Tommins rowan.collins@gmail.com
wrote:
The problem with your way of writing code is that it is ambiguous in
meaning, which is why this is a warning.I think that's a good way of looking at it. There's actually quite a lot
of code hiding a check like "if ($array['key'])"; roughly speaking, it's
short-hand for:if ( array_key_exists('key', $array) && $array['key'] !== null &&
$array['key'] !== false && $array['key'] !== 0 && $array['key'] !== 0.0
&& $array['key'] !== '' && $array['key'] !== [] )
Agreed, but you forgot the most "annoying":
&& $array['key'] !== '0'
Now, if that's what you intended, there's a syntax that's slightly more
explicit while still being reasonably short: the empty() pseudo-function:if ( ! empty($array['key']) )
On the other hand, if what you actually meant was this:
if ( array_key_exists('key', $array) && $array['key'] !== null )
Then you might have some bugs lurking, and actually want the isset()
pseudo-function instead:if ( isset($array['key']) )
For other cases, you do have to spend a few more characters to be
explicit, often using the "??" operator; for instance, if you expect
$array['key'] to be either unset or a boolean, and want this:if ( array_key_exists('key', $array) && $array['key'] === true )
Then the shortest is probably something like this:
if ( $array['key'] ?? false === true )
Mind operator precedence! This is interpreted as:
if ( $array['key'] ?? (false === true) )
(obviously not intended), so you need explicit parentheses:
if ( ($array['key'] ?? false) === true )
I will also note that the "shorthands" (!empty(), isset(), ?? null) will
also "hide" another warning when the $array variable itself is undefined
On Tue, Feb 15, 2022 at 3:07 PM Rowan Tommins rowan.collins@gmail.com
wrote:if ( array_key_exists('key', $array) && $array['key'] !== null &&
$array['key'] !== false && $array['key'] !== 0 && $array['key'] !== 0.0
&& $array['key'] !== '' && $array['key'] !== [] )Agreed, but you forgot the most "annoying":
&& $array['key'] !== '0'
Ah yes, that one really demonstrates the dangers of type juggling in
general. If it wasn't considered empty, that would be inconsistent
with how integer juggling works, but it definitely catches people out!
Mind operator precedence! This is interpreted as:
if ( $array['key'] ?? (false === true) )
(obviously not intended), so you need explicit parentheses:
if ( ($array['key'] ?? false) === true )
Oops, I admit I didn't test that. That does make the operator somewhat
less attractive, and maybe it would be more straight-forward to just be
explicit:
if ( isset($array['key']) && $array['key'] === true )
I will also note that the "shorthands" (!empty(), isset(), ?? null) will
also "hide" another warning when the $array variable itself is undefined
Ah, yes, so they do; as does the ?? operator, for that matter.
I seem to remember someone proposing an explicit operator for "this
array dimension might not exist", something like this:
if ( $array[?'key'] === true )
Which would translate to:
if ( (array_key_exists('key', $array) ? $array['key'] : null) === true )
Regards,
--
Rowan Tommins
[IMSoP]
As it is explained in this ticket https://bugs.php.net/bug.php?id=81417 we use to check if a property exists by accessing it directly like we do in JavaScript.
Personally, I subscribe to this coding style and we use it all over our codebase (more than 130 000 lines of PHP code). When it became a notice, we disabled them, but now that it is a warning, it is a lot more problematic for us… We can’t even use the last version of PHP Debug for VSCode.
The problem with your way of writing code is that it is ambiguous in
meaning, which is why this is a warning. You are not checking if a key
in an array exists, you are checking if an array value is "false-y". If
the value is an empty string, it is also interpreted as false, if it is
null, it is also interpreted as false, if it is an integer of value zero
it is also interpreted as false, if it is the string "0" it is also
interpreted as false.When you write code as you do, it is easy to introduce subtle errors
because of these implicit casts. If you want to check if an array key is
defined, you should do it explicitly - this is not about a "coding
style", this is about writing the code in a way that actually does what
you intend it to do without it meaning multiple things of which some are
probably not expected. If you just want a quick fix without solving the
underlying problem, you can just add "?? null" to each array key access.If you want to upgrade/improve your code base, which is a worthwhile
task, I would suggest the use of a static analyzer (like Psalm or
PHPStan) to find out where your code is ambigious. I have found many
undetected errors that way, where implicit casts have lead to unexpected
outcomes, which is why I am very much in favor of these warnings for
undefined array keys.
As a data point, TYPO3 is a roughly 500,000 line code base. (Non-comment lines of code as reported by phploc.) It also relied very heavily on the old "silently ignore missing values and pretend it's a null" behavior, which broke badly in PHP 8.
It took one person (me) about 3 weeks I think of running unit tests, adding ?? in various places (or array_key_exists, or whatever made sense in context), running tests again, etc. to fix nearly all of them. There's still a few that pop up now and again that didn't have test coverage, but they're rare. And that's me doing it all 100% manually, no Rector or similar automation tools.
Yes, there is work required for this change. However, with a code base 1/4 the size, and using better automation tools than I did you should be able to address all the upgrade issues in less than one person-week.
And that's without even getting into the question of array-centric code with properties being maybe-undefined is already a code smell, and has been since PHP 4. (There's been a lot of very smelly code from the PHP 4 era, but it was smelly even then.) Even without any (probably good) changes to the architecture or business logic of the application, this would improve the quality of the application.
I'd wager it's less work in terms of raw time to just fix up the code base than it is to write, implement, debate, pass, and merge an RFC to make you not have to do so.
--Larry Garfield
So I took some time to think more about it and spend some time starting upgrading the code to see the kinds of changes which would be needed.
Now, I think it is not possible to automate this. There are too many different cases and an automated script will add some ?? null everywhere even where it is not needed… I don’t want that.
After 2 hours, no bug found (but I generated one by translating the code) and I don’t find the code more readable. Here are some examples of changes:
if ($options['isCacheable'] === false)
if (($options['isCacheable'] ?? true) === false)
$twigVar = $pageClass->twigVars->$property;
$twigVar = $pageClass->twigVars->$property ?? null;
$data = $domains[$domain];
$data = $domains[$domain] ?? null;
$group = $data['group'];
$data = $data['group'] ?? null;
($options['masterAccount'] === 'only’)
(($options['masterAccount'] ?? null) === 'only')
if ($data['mysqlCache'] === false)
if (($data['mysqlCache'] ?? true) === false)
if ($data['allowCache'] !== false)
if (($data['allowCache'] ?? null) !== false)
if ($data['addRelations'] === true)
if (($data['addRelations'] ?? false) === true)
if ($options['ignoreMenu'] AND !$options['recursive']) {
if (!empty($options['ignoreMenu']) AND empty($options['recursive']))
if ($data['guid'] AND !$data['expandEvents']) {
if (isset($data['guid']) AND (!isset($data['expandEvents']) OR !$data['expandEvents’])) {
Larry Garfield said it took him 3 weeks to make the changes. It had more lines of code, but it does not mean it will be faster for me as I used this everywhere and we can’t know if there is less or more occurrences to change in my code base.
Anyway, I really don’t see the benefits to spend weeks doing this. It may highlight bugs, but it’s not sure and for me it does not worth it at all...
If I started a new project, I agree it is a good practice, but for a legacy project, this warning is just a lot of pain… I’m sure there are other projects which uses this coding style and did not wake up yet. This behavior might be the thing which will stop them from moving to PHP 8. At least for us, it is.
I took a look at the PHP code source and it seems there are just two lines of code which causes this behavior…
For me and for other PHP users with legacy code in the same distress, I’m ready to go into the process of an RFC to add a setting somewhere.
Let me know if this is something you would support. From your last answer, I guess not :-(
Le 15 févr. 2022 à 17:41, Larry Garfield <larry@garfieldtech.com mailto:larry@garfieldtech.com> a écrit :
As it is explained in this ticket https://bugs.php.net/bug.php?id=81417 https://bugs.php.net/bug.php?id=81417 we use to check if a property exists by accessing it directly like we do in JavaScript.
Personally, I subscribe to this coding style and we use it all over our codebase (more than 130 000 lines of PHP code). When it became a notice, we disabled them, but now that it is a warning, it is a lot more problematic for us… We can’t even use the last version of PHP Debug for VSCode.
The problem with your way of writing code is that it is ambiguous in
meaning, which is why this is a warning. You are not checking if a key
in an array exists, you are checking if an array value is "false-y". If
the value is an empty string, it is also interpreted as false, if it is
null, it is also interpreted as false, if it is an integer of value zero
it is also interpreted as false, if it is the string "0" it is also
interpreted as false.When you write code as you do, it is easy to introduce subtle errors
because of these implicit casts. If you want to check if an array key is
defined, you should do it explicitly - this is not about a "coding
style", this is about writing the code in a way that actually does what
you intend it to do without it meaning multiple things of which some are
probably not expected. If you just want a quick fix without solving the
underlying problem, you can just add "?? null" to each array key access.If you want to upgrade/improve your code base, which is a worthwhile
task, I would suggest the use of a static analyzer (like Psalm or
PHPStan) to find out where your code is ambigious. I have found many
undetected errors that way, where implicit casts have lead to unexpected
outcomes, which is why I am very much in favor of these warnings for
undefined array keys.As a data point, TYPO3 is a roughly 500,000 line code base. (Non-comment lines of code as reported by phploc.) It also relied very heavily on the old "silently ignore missing values and pretend it's a null" behavior, which broke badly in PHP 8.
It took one person (me) about 3 weeks I think of running unit tests, adding ?? in various places (or array_key_exists, or whatever made sense in context), running tests again, etc. to fix nearly all of them. There's still a few that pop up now and again that didn't have test coverage, but they're rare. And that's me doing it all 100% manually, no Rector or similar automation tools.
Yes, there is work required for this change. However, with a code base 1/4 the size, and using better automation tools than I did you should be able to address all the upgrade issues in less than one person-week.
And that's without even getting into the question of array-centric code with properties being maybe-undefined is already a code smell, and has been since PHP 4. (There's been a lot of very smelly code from the PHP 4 era, but it was smelly even then.) Even without any (probably good) changes to the architecture or business logic of the application, this would improve the quality of the application.
I'd wager it's less work in terms of raw time to just fix up the code base than it is to write, implement, debate, pass, and merge an RFC to make you not have to do so.
--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php <https://www.php.net/unsub.php
and I don’t find the code more readable. Here are some examples of changes:
The example changes you provide are what many people here would
recommend as good practice to improve the context and safety your code
provides.
Anyone looking at that code can immediately infer that there is an
expectation that those keys may be missing, with a suitable default
provided for if that is the case.
The engine can see this too, which avoids it emitting warnings.
It takes a little more code, yes. But that is because you're being more
clear in your intent.
This is a good thing. You should keep at it.
You could probably have the majority of your codebase converted before
you could even hold a vote. Even if the vote was passed, which seems
most unlikely, you would still need to wait 10 months for 8.2 to come
out to use it.
As I’m not ready to spend weeks upgrading our codebase because our coding style is not supported by the community, I’m looking at alternatives.
I don't think you'll be in luck, and should plan to get ready.
As to the specific behaviour:
If anything this behaviour will get tighter over time, and it's entirely
possible that in a later major release it will throw an error (although
that's not being proposed right now).
From my conversations, there is little apetite to add additional INI
based controls for engine behaviour, as they are believed to lead to
inconsistencies that harm the ecosystem, rather than enhance it.
Forking PHP to avoid resolving ambiguities in your code seems like it
would be a false economy.
Ideally you should bring your code up-to-date with what is expected, but
if you are unwilling or unable to do that, you may instead wish to stay
on an older version that hax more lax behaviour, and purchase LTS from
the likes of Zend.
Mark Randall