WordPress / WordPress-Coding-Standards

PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions
MIT License
2.56k stars 487 forks source link

Core: Ban Yoda conditions, loose comparisons and assignments in conditions #1624

Closed jrfnl closed 1 year ago

jrfnl commented 5 years ago

Is your feature request related to a problem?

Yoda conditions are the practice to reverse how comparisons in conditions are written, having the value being compared to on the left hand of the condition and the variable on the right hand, to prevent accidentally assigning something to a variable in a condition.

// This condition:
if ( $variable === 'value' ) {}

// ...would be written like this in Yoda style:
if ( 'value' === $variable ) {}

// ... to prevent this:
if ( $variable = 'value' ) {}

The problem with this is that code readability is decreased significantly to prevent a beginner's mistake which shouldn't be made in the first place and the chances of which are small when using strict comparisons anyhow.

Now, the WordPress coding standards contains a few sniffs related to this:

Note: none of these sniffs currently contain (auto-)fixers.

Describe the solution you'd like

At this moment, I'd like to propose the following:

Impact

Once these changes have been made to WPCS and Core has upgraded, contributors can be encouraged to start looking at the newly detected issues and to fix up the existing (non-strict) comparisons. This should be done carefully as changing the type of comparison is a behavioural change. It is recommended to make sure that all changes are covered by unit tests before making this type of change.

Next phase

As a next step, I'd like to propose the handbook to be adjusted to forbid Yoda conditions.

A new sniff will need to be written to detect the use of Yoda Conditions, throw an error and where possible, auto-fix this.

Alternatively it could be considered to add the Slevomat Coding Standard as a dependency to WPCS and use the existing sniff in that standard. The downsides of that would be:

Additional context (optional)

Some discussion about this has been had in other channels. I'm quoting some relevant parts of that discussion here.

@jrfnl Regarding Yoda conditions - we've talked about this before. IMO this rule should be dropped in favour of the "No assignment in condition" rule and the associated sniff WordPress/Generic.CodeAnalysis.AssignmentInCondition which is currently in Extra should be moved to Core.

At the time you asked me to investigate whether there is a sniff available out there which could revert existing Yoda conditions. The answer to that is "Yes". The external Slevomat Coding Standard contains a sniff which can auto-fix Yoda to non-Yoda. Tests could (should) be run to see how well it works, but this sniff could be used in a one-time action to move away from Yoda. Note: if the sniff would not be good enough/would turn out to be buggy, I'm happy to contribute fixes to the Slevomat standard just to make moving away from Yoda possible.

Having said that, as the tooling is available which would allow us to move away from Yoda and still prevent assignments in conditions, I would strongly suggest now is a good time to take a decision on this.

Source: https://core.trac.wordpress.org/ticket/45934#comment:14

@pento Yah, I agree that WordPress/Generic.CodeAnalysis.AssignmentInCondition should be moved into Core, violations should be fixed, and then Yoda conditions should be removed. There's some discussion on #42885 about Yoda conditions, too.

Notably, the JS team are planning on removing guidance on Yoda conditions, I'm inclined to disallow Yoda conditions, rather than simply allow either style, though.

Source: https://core.trac.wordpress.org/ticket/45934#comment:15

Opinions ?

pento commented 5 years ago

I'm in favour of this change.

There are 559 violations of WordPress.CodeAnalysis.AssignmentInCondition in WordPress Core.

This seems like something that could be auto-fixed, though. Move the assignment to before the offending block, and replace the assignment in the condition to just the variable name.

For example:

if ( $foo = bar() ) {
    // ...
} elseif ( $baz = bat() ) {
    // ...
}

Would become:

$foo = bar();
$baz = bat();
if ( $foo ) {
    // ...
} elseif ( $baz ) {
    // ...
}

This could be a problem if bat() should only over be run if bar() returned something false-y, due to side-effects or whatever. Arguably such code is fundamentally broken, anyway. πŸ™‚

It would also break on this code:

if ( $foo = bar() ) {
    // ...
} elseif ( $foo = bat() ) {
    // ...
}
jrfnl commented 5 years ago

@pento IMO assignments in condition should not be auto-fixed. A tool can not determine whether this is an assignment which should be moved out of the condition or an accidental assignment while a comparison was intended.

While I hope and presume that that last type doesn't exist in Core, we won't know for sure until we look at them.

Also, the complexity would be too high for an auto-fixer. Think:

if ( isset($foo) && ! ($bar = function_call($foo)) && $bar > 3) {}

Or:

while ( ($foo = function_call($bar)) !== false) {
    // Do something
}

Note: for assignments in while conditions, the sniff has a separate error code to allow selectively whitelisting those, as at times, that is a valid way of writing the code, though changing the code to a do {} while() or other type of control structure should probably be preferred.

GaryJones commented 5 years ago

I'm in favour of these changes as well.

Just stating something that may already be obvious - dropping the Yoda check will result in a drop in reported violations, but later adding in a check to highlight anti-Yoda statements will likely significantly bump it up again until they are fixed.

2ndkauboy commented 5 years ago

I agree that removing the Yoda Condition check could be removed from the Core ruleset, but it should not be removed from the "PHP Coding Standards" in the handbook and should still be checked in the WordPress and/or WordPress-Extra ruleset.

jrfnl commented 5 years ago

@2ndkauboy That is not how it works. The Core ruleset represents the automatic checks for what's in the PHP Coding Standards handbook.

NielsdeBlaauw commented 5 years ago

A new sniff will need to be written to detect the use of Yoda Conditions, throw an error and where possible, auto-fix this.

Alternatively it could be considered to add the Slevomat Coding Standard as a dependency to WPCS and use the existing sniff in that standard.

Would it be an option to add detection sniff in WPCS, and use a composer suggest with message that SlevomatCodingStandard.ControlStructures.DisallowYodaComparison can be used as a fixer.

Looking at the Slevomat Coding standard it appears the fixer is far more complex than the sniff part of the implementation.

I think most projects would likely only be using it once during onboarding/initial scan. After that it's enough to be warned about the instance, an it would be natural to not type yoda conditions in the first place.

JJJ commented 5 years ago

Anecdotally, I believe Yoda conditions improve code quality & readability.

They are a decades old practice with a proven track record at avoiding accidental assignment errors.

Removing them is removing the steel toes from our workboots because they aren’t comfortable.

Evidently my feelings here aren’t the currently popular ones, but I’m a big -1 on switching away from them.

GaryJones commented 5 years ago

Removing them is removing the steel toes from our workboots because they aren’t comfortable.

The presence of the assignment in condition check, means we've already removed the chance of anything heavy landing on our feet, so the steel toe caps would be redundant πŸ˜„

JPry commented 5 years ago

I'm with @JJJ on this. I don't see any need or improvement to switching away from Yoda conditions.

The problem with this is that code readability is decreased significantly

How is readability decreased, especially "significantly"? Are there references somewhere that aren't included here that can be linked to?

Even if the preference against Yoda conditions continues, I think there's no reason to completely reverse stance and start saying that Yoda conditions are bad practice.

pento commented 5 years ago

Having thought about this some more, as well as discussing it with a range of different people, I'm not sure that banning Yoda conditions entirely is the most reasonable next step.

@JJJ was kind enough to go into the detail of how he finds Yoda conditions easier than non-Yoda to understand. @nacin mentioned the special case of checking false === strpos( ... ), and the like.

So, I'd like to explore this further before making a change. @davidakennedy recently published an interesting post about designing for accessibility, which I think also applies to this conversation. Let's work out what our constraints are, then make a change that works as well as possible for everyone.

I'm writing a post for make/core about WordPress coding standards changes with relation to the PHP version bump, so I'll include a call for feedback on the topic of Yoda conditions there.

jrfnl commented 5 years ago

@pento What about the initial step ?

Should any or all of these be actioned yet ?

Note: each of these three points can be actioned independently of the others.

GaryJones commented 5 years ago
  • Removing the yoda conditions rule, without replacing it with a ban (i.e. no opinion, both yoda and non-yoda allowed);

To state what might be obvious - the decision to follow Yoda conditions in Core could be set in Core's ruleset or WordPress-Core, even if WordPress-Extra removes this requirement.

pento commented 5 years ago

@jrfnl: Still working on the post about these. πŸ™‚ Let's not action any of them yet, I suspect that 2 and 3 can happen quickly, but 1 will need some more information on the best way forward.

jrfnl commented 5 years ago

@pento OK, thanks for the heads-up. I will remove this from the WPCS 2.1.0 milestone for now.

curtisbelt commented 4 years ago

@pento

I'm writing a post for make/core about WordPress coding standards changes with relation to the PHP version bump, so I'll include a call for feedback on the topic of Yoda conditions there.

Can you provide link to the post you mentioned? Just trying to trace the progress of this issue. Thanks!

pento commented 4 years ago

@CurtisBelt: The post I was referring to was published here, with a followup posted a little later.

You'll note that Yoda conditions weren't mentioned in either post. On further reflection while writing the posts, that topic had the potential to be too much of a distraction from the more immediate changes that could happen. Unfortunately, I never got back to writing a post just for the Yoda conditions discussion.

I'm currently taking a break from working on WordPress Core, so I don't expect to have time to look at this in the near future. Given that the next step is to explore the topic further in a wider discussion, this is something that anyone interested could drive, it certainly doesn't require my input to move it along. I would suggest bringing it up at the next core dev chat as a way to gauge interest in a wider discussion.

ggedde commented 4 years ago

I really just think the Yoda conditions need to be removed. I am all for keeping loose comparisons and assignments in conditions warnings. I agree with @GaryJones that by having loose comparisons and assignments in conditions is enough to also prevent accidental typos with "=" and "==".

Luc45 commented 3 years ago

Sidenote: Yoda conditions can be automatically converted to regular conditions with PHP CS Fixer as well:

'yoda_style' => [
    'equal' => false,
    'identical' => false,
    'less_and_greater' => false,
],
mitogh commented 2 years ago

Have we considered the fact to only enforce it when using 2 equal operators and ignore it when using a strict comparison with 3 equal operators instead?

// Enforce it if
if ( $value == false ) {
}

// Ignore if
if ( $value === false ) {
}

Primarily because based on:

In the above example, if you omit an equals sign (admit it, it happens even to the most seasoned of us), you’ll get a parse error, because you can’t assign to a constant like true. If the statement were the other way around ( $the_force = true ), the assignment would be perfectly valid, returning 1, causing the if statement to evaluate to true, and you could be chasing that bug for a while.

Source

When using strict operator the premise from the docs is no longer valid as you are ensuring that won't happen :)

sirbrillig commented 2 years ago

Have we considered the fact to only enforce it when using 2 equal operators and ignore it when using a strict comparison with 3 equal operators instead?

That's a reasonable idea, but in that case why not enforce it only if using one equals operator (which really is the bug this rule is trying to prevent)?

// Enforce it if
if ( $value = false ) {
}

// Ignore it if
if ( $value == false ) {
}

// Ignore if
if ( $value === false ) {
}

The standard is there to make sure that a typo isn't made on a comparison, but automated linting can do that instead, and generally better than humans.

mitogh commented 2 years ago

That's a reasonable idea, but in that case, why not enforce it only if using one equals operator (which really is the bug this rule is trying to prevent)?

Mostly for backward compatibility and to make sure when updating to a new rule system existing rules are no longer valid or considered instead. But I do agree if this can be automated I don't see a practical reason why not to replicate it in 2 comparison operators as well.

The standard is there to make sure that a typo isn't made on a comparison, but automated linting can do that instead, and generally better than humans.

:100: on board with this idea.

jrfnl commented 2 years ago

May I suggest reading the original proposal ? As that's kind of what was originally suggested.

sirbrillig commented 2 years ago

There must be some axiom that says that if a discussion thread gets long enough it will loop back over itself. 😁

mitogh commented 2 years ago

May I suggest reading the original proposal ? As that's kind of what was originally suggested.

Well partially, from the main proposal is actually requesting a ban which was updated here: https://github.com/WordPress/WordPress-Coding-Standards/issues/1624#issuecomment-470821000

Maybe should the main ticket description should be updated for a more simple approach instead as you described here: https://github.com/WordPress/WordPress-Coding-Standards/issues/1624#issuecomment-470821000

JJJ commented 2 years ago

Please do not standardize against Yoda conditions.

Luc45 commented 2 years ago

+1 to non-yoda conditions. I find yoda counter-intuitive and hard to read, as imagine you could.

Perhaps we could open a poll to settle this?

JJJ commented 2 years ago

WordPress standardized on Yoda conditions 15 years ago.

Every plugin & theme has inherited that standard, resulting in verifiably millions of lines of PHP code that are written this way.

A timeline of future events after this change happens:

If this changes, the larger WordPress landscape is left littered with the simplest code imaginable (variable comparisons) looking different everywhere, in every project, all over the web. A few people will be happy and feel accomplished, and everyone will suffer.

Changing this results in code-slurry across the web that is marginally easier for a vocal minority to ingest, but will be highly disorienting for everyone else – I.E. people like my brilliant wife who is, as I write this, learning PHP for her very first time and is constantly tripping over (and frustrated by) uselessly differing code structure.

It will be a mistake to the entire ecosystem to intentionally invert such a prolific WordPress'ism.

(Edit: ADHD over-explanation incoming – I personally know & believe everyone here is saying & doing what they feel is best & right. I also know that, perhaps, I am wrong. What I'm asking for here is some acknowledgement of the possibility that this standard was set for important reasons then, that those reasons are still important today, and that flipping it 180 degrees around has a non-zero chance of making everything worse.)

anomiex commented 2 years ago

If this changes, the larger WordPress landscape is left littered with the simplest code imaginable (variable comparisons) looking different everywhere, in every project, all over the web.

On the other hand, the larger PHP ecosystem seems to generally be non-Yoda. From this perspective WordPress seems like the outlier.

Then again, the larger PHP ecosystem has also standardized on PSR-4 for naming files containing classes, camelCase for method and variable names, and short array syntax. I've not seen any other project that requires aligning the => in arrays or = in assignments. And few projects seem to have clung to PHP 5.6 for so long.

dingo-d commented 2 years ago

@JJJ just because something was added 15 years ago, doesn't mean it should stay this way forever.

I don't think that the argument that various plugins/themes/projects out there having different coding standards (the reality is that the majority doesn't have any) should prevent changes in the WordPress core coding standards.

If anything it would be better if we are doing what the rest of the PHP community is doing and see if we can somehow align with their standard.

That way WordPress wouldn't be the odd one out IMO.

Plus, the point of using automated tools such as PHPCS is to prevent issues that Yoda conditions solved back when we didn't have such tools (or didn't use them).

Luc45 commented 2 years ago

A few people will be happy and feel accomplished, and everyone will suffer.

easier for a vocal minority to ingest, but will be highly disorienting for everyone else

I have downloaded almost all the 93 965 plugins in the WordPress Plugin Repository (Due to network gaps, I was able to download 82 450 plugins directly from wordpress.org servers (93GB)) and ran PHPCS to find the usage of Yoda vs Non Yoda conditions. This provides us with data that brings a pragmatic insight that can be beneficial to the discussion.

I will try to be as transparent, pragmatic, and scientific as possible, to describe every inch of my methodology so that it can be peer-reviewed to make sure it does not contain any bias.

This is the phpcs.xml that I have used:

<?xml version="1.0"?>
<ruleset name="Yoda Analyzer">
    <config name="installed_paths" value="/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/phpcs/vendor/slevomat/coding-standard"/>

    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/0-delay-late-caching-for-feeds</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/0-errors</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/001-prime-strategy-translate-accelerator</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/002-ps-custom-post-type</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/011-ps-custom-taxonomy</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/012-ps-multi-languages</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/028-ps-combine-taxonomy-children</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/030-ps-display-upload-path-for-wp35</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/03talk-community-conference</file>
    <file>/home/lucas/local/data/www/plugin-directory/WordPress-Plugin-Directory-Slurper/plugins/0gravatar</file>
    <!-- ... etc, until all plugins are scanned (in batches) -->

    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*/freemius/*</exclude-pattern>
    <exclude-pattern>*/vendor_prefixed/*</exclude-pattern>
    <exclude-pattern>*/composer/*</exclude-pattern>

    <arg name="extensions" value="php"/>

    <arg name="parallel" value="12"/>

    <rule ref="SlevomatCodingStandard.ControlStructures.DisallowYodaComparison"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.RequireYodaComparison"/>
</ruleset>

I have tried to do my best to exclude any vendor code, such as packages that might be installed using composer, and focus only on the WordPress PHP code of the plugin.

I've used PHPCS's --report=source option, which gives me a result such as this (this example is for the first 10k scans):

PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
--------------------------------------------------------------------------------------------
    STANDARD  CATEGORY            SNIFF                                                COUNT
--------------------------------------------------------------------------------------------
[x] Slevomat  Control structures  Require yoda comparison required yoda comparison     426417
[x] Slevomat  Control structures  Disallow yoda comparison disallowed yoda comparison  100539
[ ] Internal                      No code found                                        1851
[ ] Internal                      Line endings                                         1475
[ ] Internal                      Exception                                            6
--------------------------------------------------------------------------------------------
A TOTAL OF 530288 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES
--------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (526479 VIOLATIONS IN TOTAL)
--------------------------------------------------------------------------------------------

Time: 7 mins, 3.01 secs; Memory: 56.24MB

I had to split the PHPCS run into batches, as it was too much for my PC to process in one go. To do that, I wrote a small script that generates a phpcs.xml with plugins within a range.

Results per batch **Plugins 0 to 10k:** ``` ➜ phpcs git:(master) βœ— cat 0_10000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 426417 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 100539 [ ] Internal No code found 1851 [ ] Internal Line endings 1475 [ ] Internal Exception 6 -------------------------------------------------------------------------------------------- A TOTAL OF 530288 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (526479 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 7 mins, 3.01 secs; Memory: 56.24MB ``` **Plugins 10k to 20k:** ``` ➜ phpcs git:(master) βœ— cat 10000_20000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 418345 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 91076 [ ] Internal No code found 2743 [ ] Internal Line endings 1233 -------------------------------------------------------------------------------------------- A TOTAL OF 513397 SNIFF VIOLATIONS WERE FOUND IN 4 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (509083 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 7 mins, 0.65 secs; Memory: 54.12MB ``` **Plugins 20k to 30k:** ``` ➜ phpcs git:(master) βœ— cat 20000_30000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 445222 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 92608 [ ] Internal Line endings 2012 [ ] Internal No code found 1590 [ ] Internal Exception 2 -------------------------------------------------------------------------------------------- A TOTAL OF 541434 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (537569 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 7 mins, 37.34 secs; Memory: 54.27MB ``` **Plugins 30k to 40k:** ``` ➜ phpcs git:(master) βœ— cat 30000_40000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 458963 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 101565 [ ] Internal Line endings 3784 [ ] Internal No code found 2689 [ ] Internal Exception 7 -------------------------------------------------------------------------------------------- A TOTAL OF 567008 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (560188 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 6 mins, 50.86 secs; Memory: 58.91MB ``` **Plugins 40k to 50k:** ``` ➜ phpcs git:(master) βœ— cat 40000_50000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 479307 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 96621 [ ] Internal Line endings 3196 [ ] Internal No code found 1631 [ ] Internal Exception 1 -------------------------------------------------------------------------------------------- A TOTAL OF 580756 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (575509 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 7 mins, 33.55 secs; Memory: 62.96MB ``` **Plugins 50k to 60k:** ``` ➜ phpcs git:(master) βœ— cat 50000_60000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 364109 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 82842 [ ] Internal No code found 1617 [ ] Internal Line endings 1123 -------------------------------------------------------------------------------------------- A TOTAL OF 449691 SNIFF VIOLATIONS WERE FOUND IN 4 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (446646 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 6 mins, 25.88 secs; Memory: 47.55MB ``` **Plugins 60k to 70k:** ``` ➜ phpcs git:(master) βœ— cat 60000_70000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 472825 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 106476 [ ] Internal Line endings 1831 [ ] Internal No code found 1762 [ ] Internal Exception 4 [ ] Internal Tokenizer 1 -------------------------------------------------------------------------------------------- A TOTAL OF 582899 SNIFF VIOLATIONS WERE FOUND IN 6 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (578880 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 8 mins, 5.99 secs; Memory: 58.7MB ``` **Plugins 70k to 80k:** ``` ➜ phpcs git:(master) βœ— cat 70000_80000.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY ----------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT ----------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yod 549068 [x] Slevomat Control structures Disallow yoda comparison disallowed 120069 [ ] Internal No code found 1731 [ ] Internal Line endings 1428 [ ] Internal Exception 6 ----------------------------------------------------------------------------- A TOTAL OF 672302 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES ----------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (668881 VIOLATIONS IN TOTAL) ----------------------------------------------------------------------------- Time: 9 mins, 26.69 secs; Memory: 63.78MB ``` **Plugins 80k to 82450:** ``` ➜ phpcs git:(master) βœ— cat 80000_82450.txt PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------------------- [x] Slevomat Control structures Require yoda comparison required yoda comparison 166025 [x] Slevomat Control structures Disallow yoda comparison disallowed yoda comparison 38594 [ ] Internal No code found 536 [ ] Internal Line endings 251 [ ] Internal Exception 1 -------------------------------------------------------------------------------------------- A TOTAL OF 205407 SNIFF VIOLATIONS WERE FOUND IN 5 SOURCES -------------------------------------------------------------------------------------------- PHPCBF CAN FIX THE 2 MARKED SOURCES AUTOMATICALLY (204464 VIOLATIONS IN TOTAL) -------------------------------------------------------------------------------------------- Time: 3 mins, 38.64 secs; Memory: 24.75MB ```

This is the final result:

My thoughts:

Looking at the data, I don't see a cascading effect on the entire world wide web that could bring doom and calamity to the internet if we stop using Yoda in Core. The Codex itself has a funny statement on the "Yoda" section: A little bizarre, it is, to read. Get used to it, you will., which seems to go against what it says just a couple lines below, on the "Clever Code" section: In general, readability is more important than cleverness or brevity..

To be quite frank, I think I'm dumber than most people. I really do. I don't have a very high cognitive capacity. That's why for me it's very important that the code is easy to read, follow, and understand. My personal opinion is that it compromises readability, and this is pretty much the consensus of Yoda conditions on the internet for the majority. People will not agree on everything, but we can pretty much confidently say that the majority prefers regular conditions, so I think this is a pretty pertinent discussion to have.

JPry commented 2 years ago

After commenting before in favor of keeping Yoda conditions, I feel like my stance has changed slightly. I think the best way forward is to:

  1. Allow both styles of conditionals, Yoda and "regular".
  2. As initially described, an assignment within a conditional should be an error.
  3. Loose (==) operators should be a warning, as they are likely to be the source of sneaky bugs.

For item 1, I think that while the discussion around readability is completely relevant, the fact remains that we're talking about comparing two values. Is it really a readability issue to distinguish between 'a' === $b and $b === 'a'? Comparisons like this should be equally easy (or difficult) for a developer to parse regardless of which item is first.

To Yoda or not to Yoda feels like it is getting far more controversial than it really deserves, and perhaps the best solution is to switch from enforcing it one way or the other to taking no stance at all on it. Instead, let's just focus on truly solving the problem that Yoda conditions originally intended to solve: assignments within conditionals.

rene-hermenau commented 2 years ago

nd, the larger PHP ecosystem seems to generally be non-Yoda. From this perspective, WordPress seems like the outlier.

That's an impressive test that proves that Yoda conditions are not accepted or used by most developers.

However, Yoda conditions were implemented at a time when they were indeed useful. Going back one, two, three decades, not many developers used a full-fledged IDE or a smart editor with code inspection built-in. Code was written often without any assistance from the editor.

Today it's common for us to use a coding editor that helps us write better code, including telling us when we make an obvious error like assigning a variable instead of comparing it.

Twenty years ago, when I was a beginner, it was hard for me to get into Yoda, but today, it does not make a difference for me in reading it. But considering the pain I had as a beginner, I'd like to save other beginners from having the same experience so I vote for the human-friendly readability of regular styles.

Side note: One of my colleagues reminded me of the yoda style's readability aspect, and we switched the entire code base of our bigger project a while ago. It's much more fun to read and work with the code nowπŸš€

These are the little things you appreciate only if you start applying them. Before you do, they are not really important. It doesn't matter much and does not give much value that we can measure and quantify, but when it's done, it can have a significant positive impact on the whole coding experience... even for experienced developers.

JJJ commented 2 years ago

Thanks @Luc45 πŸ’œ I wasn't intentionally fear-mongering, but certainly unintentionally, and the data is extremely helpful to see. Relatedly, I hadn't deeply considered what the greater PHP community had decided, so that's also compelling.

Consider me persuaded. Please accept my embarrassed apologies for disrupting progress here, and you have my gratitude for being patient with my resistance.


Edit: wanna address one thing, sincerely:

just because something was added 15 years ago, doesn't mean it should stay this way forever.

When existing conditions cause problems, new conditions should resolve old problems without introducing new problems.

Change needs to have a reason, and the pros of that reason need to outweigh the cons of tolerating existing conditions.

If change is ambiguous or preferential, it is not better than a guess or an opinion.

Regretfully, I fell into the opinion-trap, and I'm sorry if I was dismissive of anyone's perspectives. πŸ’™

Luc45 commented 2 years ago

@JJJ it is with good intentions and divergent opinions that we get to the best solutions, always. If we all agreed, we would be missing the opportunity to seeing things from another angle, which could be dangerous. So thank you for disagreeing before, and for being open to the discussion.

kraftbj commented 2 years ago

Wanted to voice support for @JPry's comment above

Allow both styles of conditionals, Yoda and "regular". As initially described, an assignment within a conditional should be an error. Loose (==) operators should be a warning, as they are likely to be the source of sneaky bugs.

I don't care if WPCS does end up banning yoda conditionals or not, but I think the initial step of not trying to force code to use yoda is a good step forward.

dingo-d commented 2 years ago

In the context of WordPress Core, allowing both is a disaster IMO. Either use one or the other. Allowing both to be in the core just adds additional confusion for new developers to the project. Should it be Yoda or not? When? Why?

We should be consistent across the codebase.

I think people confuse WPCS standards as set in stone when it comes to building their own themes or plugins. And that's not the case. You can use whatever you want in your project. You can use parts of the WPCS ruleset, remove some, leave the one you want.

Personally, I only use some functional aspects of WPCS in my projects, while removing almost all of the styling CS, because I prefer PSR-12 and PSR-4 for autoloading.

I think this issue could be talked about in a meeting and discussed in official make posts for more visibility. Maybe throw in a poll to get the wider audience reach.

JPry commented 2 years ago

@dingo-d

In the context of WordPress Core, allowing both is a disaster IMO. Either use one or the other. Allowing both to be in the core just adds additional confusion for new developers to the project. Should it be Yoda or not? When? Why?

I disagree. As I mentioned above, the discussion around whether Yoda style is bad or good seems to be overshadowing the original purpose of why the style was put in place. Developer confusion only comes into play when trying to explain why one way is "better" than the other, or why it's even enforced at all. Otherwise, $a === 'b' is just as easy to understand as 'b' === $a.

jrfnl commented 2 years ago

Otherwise, $a === 'b' is just as easy to understand as 'b' === $a.

As a person with dyslexia, I strongly disagree with this, the second form takes me about three times as much time to process as the first.

I do agree that the original goal of why Yoda was initially enforced has been superseded and that that should be addressed first.

As for consistency in the WP Core code base after the Yoda restriction has been removed, let's leave that for a future iteration.

For plugin/theme repos, they can choose their own rules, so even when WPCS would remove the rule, there are sniffs available (in PHPCS itself/other external standards) to either enforce no Yoda or to still enforce Yoda, which a plugin/theme can elect to add to their custom ruleset.

andronocean commented 2 years ago

I'd like to add another reason to remove the Yoda rule: it makes the sniffs noisier for no useful reason.

The sniffs only help users who install them, so anyone who doesn't and isn't contributing to Core is out of scope. If someone has installed the sniffs, they can get instant feedback on errors. The most useful feedback is warnings about assignment-in-condition and loose comparisons: those are the specific things that cause problems, and that info is helpful to novice and experienced devs alike.

Enforcing Yoda doesn't add anything to this β€” it just generates more warnings, more noise for the developer to process, and hence more overwhelm and a longer learning curve for new devs.

Luc45 commented 2 years ago

I'd like to add another reason to remove the Yoda rule: it makes the sniffs noisier for no useful reason.

The sniffs only help users who install them, so anyone who doesn't and isn't contributing to Core is out of scope. If someone has installed the sniffs, they can get instant feedback on errors. The most useful feedback is warnings about assignment-in-condition and loose comparisons: those are the specific things that cause problems, and that info is helpful to novice and experienced devs alike.

Enforcing Yoda doesn't add anything to this β€” it just generates more warnings, more noise for the developer to process, and hence more overwhelm and a longer learning curve for new devs.

I think you're confusing code style checks with static code analysis, such as PHPStan.

andronocean commented 2 years ago

I think you're confusing code style checks with static code analysis, such as PHPStan.

How so? This is all checkable through PHPCS. I can get alerts from it as I code with something like the PHP Sniffer & Beautifier extension in VS Code. No static analysis necessary, afaik.

jrfnl commented 2 years ago

Just a FYI: PHPCS === static analysis, just based on a different entry point to the code compared to PHPStan.

Luc45 commented 2 years ago

Just a FYI: PHPCS === static analysis, just based on a different entry point to the code compared to PHPStan.

True that. My reasoning when I said that was because of the intent... PHPCS uses static analysis to check code style, while PHPStan uses static analysis to check logic errors, so if by "useful error" he means "flagging logic errors in my code", PHPCS is not the tool for that, as PHPCS generally speaking is meant to enforce code style conventions that shouldn't affect the logic of the code, no matter how the code style looks like.

<offtopic/>

Luc45 commented 2 years ago

As for next steps, what about?

jrfnl commented 2 years ago

Just a FYI: PHPCS === static analysis, just based on a different entry point to the code compared to PHPStan.

True that. My reasoning when I said that was because of the intent... PHPCS uses static analysis to check code style, while PHPStan uses static analysis to check logic errors, so if by "useful error" he means "flagging logic errors in my code", PHPCS is not the tool for that, as PHPCS generally speaking is meant to enforce code style conventions that shouldn't affect the logic of the code, no matter how the code style looks like.

<offtopic/>

While maybe off topic, it does a disservice to PHPCS, which can do a lot more than just check for code style convention. Think: the PHPCompatibility standard and sniffs in the CodeAnalysis category.

Of course, there are limitations, but don't underestimate PHPCS.

As for next steps, see my previous post about this and the original topic.

Most likely, we'll need a Make post before we can put this into effect anyhow.

Other than that:

Luc45 commented 2 years ago

@jrfnl good points about the PHPCompatibility capabilities of PHPCS. A while back I needed to convert a project from Yoda to non-yoda, and PHP-CS-Fixer was the only tool that I found that could do it (Link). Indeed, I remember now that it was a separate tool from PHPCBF.

jrfnl commented 2 years ago

@Luc45 As described in the original topic, there is a PHPCS sniff in the Slevomat standard which can do this perfectly fine as well.

Luc45 commented 2 years ago

@Luc45 As described in the original topic, there is a PHPCS sniff in the Slevomat standard which can do this perfectly fine as well.

You're right, thanks. I'm replying here while working on other stuff and am a little bit distracted. I remember now that I have used that Slevomat standard when counting Yoda vs Non-Yoda in the WordPress Plugin ecosystem, actually... I agree that bumping to ^3.0.0 makes sense. ~Do you agree about banning assignments in conditions?~

I see now in the main comment of this thread that this is exactly what you proposed.

Therefore, I agree 100% with your proposal, considering a bump to ^3.0.0 and allowing people to stick to ^2 for existing projects that use Yoda and want to stick with it.

Luc45 commented 2 years ago

A good example of where Yoda can get really confusing: Inverted comparison operators:

image

I had to literally write a snippet to code review this: https://3v4l.org/oTVW0#v7.4.29

Friends, what should we do next to bring this proposal to the table? Does anyone oppose getting rid of Yoda?

2ndkauboy commented 2 years ago

A good example of where Yoda can get really confusing: Inverted comparison operators:

These were never included in the Yoda condition rules.