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

[WIP] PHPCS 3.5.0: refactor utility methods and add additional utility methods #2456

Closed

Conversation

jrfnl
Copy link
Contributor

@jrfnl jrfnl commented Mar 23, 2019

🚧 This is a draft PR to make what I've been working on for this refactor visible and accessible to interested parties. 🚧

⚠️ New commits may be added without notice.
⚠️ Existing commits may be changed/reorganized without notice.
⚠️ This PR will be rebased on master whenever relevant.
⚠️ Until this PR is changed from "Draft" to "Ready for review", all changes will be force-pushed.

Having said all that: feedback is very welcome.

Current WIP

Things I'm still working on, which may or may not be added to this PR (listed in order of the likelihood of them to make it into this PR):

  • Check for additional places where TextStrings::stripQuotes() can be used/implemented.
  • Various methods to determine if something is a function call or not.
  • Various methods to determine whether construct names comply with various typical naming conventions.
  • Implement Comments::findFunctionComment() in the Squiz.WhiteSpace.FunctionSpacing sniff.
  • Method: Comments::findFileComment()
  • Various additional comment handling related methods.
  • Class: AbstractDocblockSniff
  • Methods:TextStrings::getInterpolatedVariables() and TextStrings::stripInterpolatedVariables().
    Mostly done, but some nasty edge cases are failing.
  • Utility class: FQNames
  • Method: Variables::isAssignment()
  • Method to determine what a T_AS token is used for.

Some of the above may be pulled separately at a later point in time once this PR has been merged.


Introduction

This refactor has been done with an eye to:

  • Making it easier to add additional utility methods which can be useful for PHPCS itself as well as external standards;
  • Removing duplicate code;
  • Improving consistency of sniff results;
  • Improving maintainability of the library.

The basic constraints which this refactor tries to adhere to:

  • Prevent BC-breaks as much as possible.
  • Have a negligible or only a positive impact on the performance (time and memory usage) of PHPCS.
  • All changes to existing utility methods should be fully unit tested and contain no BC-breaks when the old/original methods are called.
    Where existing utility methods did not have dedicated unit tests yet, those have been added before making any significant changes to the methods.
  • All newly introduced utility methods should be fully unit tested.
  • All utility properties and methods in the new classes are public static and can be called from anywhere (unless specifically designed for internal/under the hood use).

Unit tests

In general, I've erred on the side of caution and have added abundant unit tests, sometimes up to the point of redundancy.

All of the newly introduced utility classes have a code coverage of > 98% based on their dedicated unit tests alone.

Where the test coverage is lower, it either involves conditions related to the availability of PHP extensions, exceptional circumstances which would only occur when there are rare parse errors/during live coding or artifacts of code coverage analysis weirdness related to function end comments being seen as code lines.

201903 PHPCS Utils code coverage

Notes:

  • Comments: The methods are 100% covered. The stray lines is PHPUnit getting confused over function end comments.
  • ConstructNames: One of the "old" methods (ConstructNames::isUnderscoreName) is not unit tested yet. Unit tests for that will be added when the additional methods to determine whether construct names comply with various naming conventions, will be added.
  • Orthography: This is due to conditions related to the availability of the MBString PHP extension.
  • UseStatements: The methods are 100% covered. The stray line is PHPUnit getting confused over a function end comment.

Performance

I've done a numbers of checks with Blackfire, but haven't been able to draw any real conclusions from these.

  • Doing a PHPCS run with the PHPCS native ruleset against PHPCS itself appears to be slower with no significant difference in memory usage.
    The slower part could be explained by the number of additional files which need to be scanned, so this is inconclusive.
  • A unit test run on the sniffs alone phpunit ./tests/Standards/AllSniffs.php shows no significant difference (< 2% difference in memory and run time which can be explained by the additional unit tests which have been added for bug fixes and enhancements to sniffs).
  • A unit test run on the Core methods alone phpunit ./tests/Core/AllTests.php shows a whopping -90% run time difference and a + 27.7% memory diff.
    The run time going down significantly is due to the changes as previously pulled in Core tests: set up abstract class and remove code duplication #2295, which are included in this PR.
    I expect the memory difference to be due to the enormous amount of extra unit tests for this run - previously 229, now 1433.

It may be warranted to do some additional performance tests, for instance running each of the PHPCS native rulesets against a large project such as WordPress using first master and then this refactor branch, to see if that provides us with conclusive results.

Reviewing the commits

All commits have been set up as atomic commits and, safe for a few which highlight underlying issues which are subsequently fixed in the commits straight after, all individual commits will pass all CI checks.

As the commits tell a story, this PR will be easiest to review per commit in the order I've placed the commits.

I've implemented the use of the new utility methods wherever I deemed appropriate based on keyword searches.

There may however be more sniffs which could benefit from the new methods. Suggestions welcome, though this can/should probably be fixed in separate PRs after this PR has gone in.

Documentation

These documents will be kept up to date with additional changes made to this branch.

Discussion points

  • The File::getMemberProperties() (now FunctionDeclarations::getMemberProperties()) method throws a warning when a property is declared in an interface (= parse error). This behaviour is currently unchanged.
    It could be argued that the parse error warning can now be removed in favour of just throwing the Exception, but that would be a BC-break for sniffs using this method and expecting to receive an empty array instead of the Exception.
    I'd suggest making that change in PHPCS 4. Also see issue Suggestion: remove warnings/errors for detected parse errors (PHPCS 4) #2455.
    Alternatively, the parse error warning could already be removed with just an empty array being returned for now.
  • The moving of the $magicMethods, $methodsDoubleUnderscore and the $magicFunctions properties from the Generic.NamingConventions.CamelCapsFunctionName and the PEAR.NamingConventions.ValidFunctionName sniffs to the new FunctionDeclarations class creates a minor BC-break.
    The properties are being recreated in their old form via the class constructors of those two sniffs, but any sniff which would extend one of those sniffs and overload the class constructor without calling the parent constructor will have to deal with a BC-break as in that case, the properties will be empty.
    As an alternative, the old, deprecated properties could be left as they were in the sniff classes for the time being and the recreation code removed.
    In that case, there would be no BC break, but the content of the properties would still be in three places in the code base.
  • Along the same lines, the moving of the $phpReservedVars from the PHP_CodeSniffer\Sniffs\AbstractVariableSniff class to the PHP_CodeSniffer\Util\Sniffs\Variables class creates a similar minor BC-break if a child class overloads the constructor without calling the parent constructor.

Changes to existing sniff unit tests

This refactor has been done in such way to avoid backward-compatibility breaks.

Nearly all sniff unit tests still pass without any adjustments to the unit test files, other than adding additional test cases for bugs which have been fixed or enhancements a sniff has received through this refactor.

The only sniff unit test files which have been adjusted are:

  • Squiz.Scope.MemberVarScope - the unit test file contained a parse error (closure as class property value in the initial declaration) which the AbstractVariableSniff accounted for.
    This particular parse error is no longer accounted for, so the sniff will now throw some additional errors in that specific situation (which shouldn't occur in real life anyway).
  • Squiz.WhiteSpace.MemberVarSpacing and Zend.NamingConvention.ValidVariableName - the unit test files contained parse errors (properties declared in an interface). The AbstractVariableSniff will no longer pass those on as "member variables".
    Traits, however, can contain properties and were so far untested for both sniffs, so I've taken the liberty to change the test cases from interfaces to traits.
  • Squiz.Arrays.ArrayDeclaration - contained a unit test related to short lists which was originally added to check for a Tokenizer issue and not to test this sniff specifically - see Short array list syntax not correctly tokenized if short array is the first content in a file #1971.
  • Squiz.Commenting.FunctionComment - contained a PSR-5 style return type which is now fixable.

For the most part, I've not removed unit tests from sniff test case files, even when the case being tested is now also tested through the dedicated utility method unit tests.

The only exception to this is for the magic method related test cases for the Generic.NamingConventions.CamelCapsFunctionName, PSR1.Methods.CamelCapsMethodName and PEAR.NamingConventions.ValidFunctionName sniffs.
For those three sniffs, I've removed most magic method related unit test cases, just leaving a few spot-checks in place.


Fixes #2189 (refactor)
Fixes #1984 (short list constructs)
Fixes #1864 (allow Squiz comment sniffs to suggest short form types)
(Maybe) Fixes #2222 (abstract docblock sniff)

Closes #2128 (extended interfaces)
Closes #2295 (core utility method testing refactor)
Closes #2353 (consistent exceptions)
Closes #2368 (MySource/AjaxComparison)


To do once this PR is accepted/merged:

  • Update the Customisable Sniff Properties wiki page with the new properties for the Squiz.Commenting.FunctionComment and Squiz.Commenting.VariableComment sniffs.

Other open action points

These can be sorted out after this PR has been merged, low prio.

  • One of the Variables "superglobal" methods should possible be used by the MySource.PHP.GetRequestData sniff, but as the specs of that sniff (or rather of the suggested method to use instead) are not clear, I have not actioned this at this time.
  • If PR Generic/FunctionCallArgumentSpacing: remove assignment operator spacing checks #2399 would be accepted, use of the new PassedParameters::getParameters() method can be implemented in the Generic.Functions.FunctionCallArgumentSpacing sniff.

jrfnl added 30 commits March 22, 2019 22:44
Refactor to remove duplicate code.

This new class abstracts out the `setUp()` and `tearDown()` methods which were included in nearly all the utility method test classes.

The methods have been changed to static `setUpBeforeClass()` and `tearDownAfterClass()` with a static property as the initial processed `File` doesn't change between tests, so there is no need to tokenize and process the unit test case file for each test individually.

This makes the utility method unit tests significantly faster as well as lower the memory usage.

Additionally, the _standard_ against which the files are processed has been changed from `Generic` (75 sniffs) to `PSR1` (7 sniffs), which again makes the unit test suite for the Core utility functions significantly faster.

The current implementation allows for only one test case file per utility method test file, but as the utility method tests are not sniff specific, adding additional tests files for a method if needed, should not be a problem.

By default test case files are expected to be PHP `inc` files. Child classes can overload the static `$fileExtension` property when the test case file is a JS or CSS file.
This adds a new `getTargetToken()` method which can retrieve the target token for testing with higher precision than the (duplicate) code which was so far used in the individual test methods.

The improvements this method offers are:
* Avoid test leaking/contamination.
    If/when the token to start the test from was retrieved by doing a `findNext()` from the delimiter comment, a typo could cause a token from the *next* test to be used for the testing instead of the target token.
    If the expected results would be the same for both tests, this would go completely unnoticed.
    This is now no longer possible.
    The only requirement is that the delimiter comments start with `/* test...`.
* No more token counting when setting up the unit tests, just pass the target token type constant to this method and it will get you the correct token.
    This also allows for not having to jump through hoops when deciding where to place the delimiter comment for the test.
* If the token a test looks for is a `T_STRING` or text based token, the optional `$tokenContent` allows for selecting the correct token, even when there are several of the same type in the test case line.
Implement use of the new AbstractMethodUnitTest::getTargetToken() method as well as abstract out the actual testing to a helper method as the logic was the same in all methods.

I've chosen not to implement this with a data provider as the separate test methods creating the `$expected` arrays make the tests more readable and easier to understand.
Implement use of the new AbstractMethodUnitTest::getTargetToken() method as well as abstract out the actual testing to a helper method as the logic was the same in all methods.

I've chosen not to implement this with a data provider as the separate test methods creating the `$expected` arrays make the tests more readable and easier to understand.
As the refactor will add a lot of new unit test files, let's automate the creation of the test suite some more by automatically adding all Test files within the `Test\Core` directory.
As far as I can see, Exceptions thrown by sniffs/utility methods when, for instance, a wrong token is passed, should be `RuntimeException`s, while Exceptions thrown by the `Tokenizer` classes should be `TokenizerException`s.

In a number of places this was applied inconsistently. This has now been fixed.

Aside from that, for quite a number of methods, the `Exception` being thrown has now been documented.
Move the methods related to condition checking out of the `File` class to a separate class with static methods.

This deprecates the `File::hasCondition()` and the `File::getCondition()` methods in favour of the same in a new `Util\Sniffs\Conditions` class.

This functionality is only used by a small percentage of sniffs, so is better placed in a dedicated class.
This also allows for additional condition related functions to be added to this dedicated class.

The deprecated methods should be removed in PHPCS 4.0.
The `File::getCondition()` and `File::hasCondition()` methods (now `Conditions::getCondition()` and `Conditions::hasCondition()`) did not have dedicated unit tests.

These have now been added.
… more versatile

This refactors the `Conditions::getCondition()` method to be more versatile with an eye on additional methods to be added later.

It also removes some duplicate code and will allow the new methods to be added without code duplication.

Includes additional unit tests covering the new functionality added to the `Conditions::getCondition()` method.
…ethods

This adds two additional utility methods:
* `getFirstCondition()` - to retrieve the first condition of a type passed in `$types` for a token or the first condition for a token if no types are specified.
    This is effectively just an alias for the `getCondition()` method.
* `getLastCondition()` - to retrieve the last condition of a type passed in `$types` for a token or the last condition for a token if no types are specified.

Includes dedicated unit tests.
This commit is two-fold:

On the one hand, it improves handling of anonymous classes which extend another class or implement an interface.
This means that the error code for those errors will now correctly indicate that the _unused function parameter_ was found in an extended class or implemented interface.

On the other hand, the sniff now recognizes that classes can be nested and will only look at the actual class in which the method is found, not at a higher level class.
This fixes a bug were the error code would be incorrect for unused parameters in nested classes.

Includes unit tests for both cases.
…fs\Conditions

This adds four additional utility methods:
* `isOOProperty()` - to check if a `T_VARIABLE` token is a class/trait property. Returns true/false.
* `isOOConstant()` - to check if a `T_CONSTANT` token is a class/interface constant.  Returns true/false.
* `is_OOMethod()` - to check if a `T_FUNCTION` token is a class/interface/trait method.  Returns true/false.
* `validDirectScope()` - to check the direct condition of an arbitrary token against a set of valid scope conditions. Returns the integer stackPtr to the direct condition if valid or false otherwise.

These methods have been battle-tested in the past year or two in the PHPCompatibility standard.

Includes dedicated unit tests.
…::getMemberProperties()

... without changing the existing behaviour.

It could be argued that the parse error warning can now be removed in favour of just throwing the Exception, but that would be a BC-break for sniffs using this method and expecting to receive an empty array instead of the Exception.

I'd suggest making that change in PHPCS 4.

Alternatively, the parse error warning could already be removed with just an empty array being returned for now.
1. Closures can not be declared as the value of a property.
    This would be a parse error and one which I don't believe the `Conditions::isOOProperty()` should need to account for.
2. Interfaces can not declare properties.
    The switch over to the `Conditions::isOOProperty()` method in the `AbstractVariableSniff` means that a variable declared as a property in an interface will no longer be seen as a property and therefore the warning which would be generated by the `File::getMemberProperties()` method will no longer be thrown.

The unit test files have been adjusted accordingly.
Interfaces cannot declared properties. Traits however can and were so far not tested.
Interfaces cannot declared properties. Traits however can and were so far not tested.

The switch over to the `Conditions::isOOProperty()` method in the `AbstractVariableSniff` means that a variable declared as a property in an interface will no longer be seen as a property and therefore the warning which would be generated by the `File::getMemberProperties()` method will no longer be thrown.

However, if the construct is changed to a `trait`, the normal logic will kick in and the `PrivateNoUnderscore` error will be thrown.
This adds ten new utility/convenience methods.

The first two are targeted at open/close parentheses tokens:
* `getOwner()` - to retrieve the stack pointer to the parentheses owner of an open/close parenthesis. Returns int or false if the parenthesis does not have an owner.
* `isOwnerIn()` - to check whether the parenthesis owner of an open/close parenthesis is within a limited set of valid owners. Returns bool.

The other eight methods are targeted at arbitrary tokens:
* `hasOwner()` - to check whether the passed token is nested within parentheses owned by one of the valid owners. Returns bool.
* `lastOwnerIn()` - to check whether the owner of a direct wrapping set of parentheses is within a limited set of acceptable tokens. Returns int/false.
* `getFirstOpener()` - to retrieve the position of the opener to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
* `getFirstCloser()` - to retrieve the position of the closer to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
* `getFirstOwner()` - to retrieve the position of the parentheses owner to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
* `getLastOpener()` - to retrieve the position of the opener to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
* `getLastCloser()` - to retrieve the position of the closer to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
* `getLastOwner()` - to retrieve the position of the parentheses owner to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.

These last eight, all use the private `nestedParensWalker()` helper method under the hood.

Where the return value is of the type `int|false`, the integer stack pointer to the parentheses opener/closer/owner will be returned or `false` when:
* the token is not wrapped in parentheses;
* the parentheses owner is not within the set of `$validOwners` passed;
* and in the case of `owner`: when the desired set of parenthesis does not have an owner.

Includes dedicated unit tests.
…class

Implement the use of the new `Parentheses::getOwner()` method in the `AbstractArraySniff` class.
Implement the use of the new `Parentheses::isOwnerIn()` method in the `Squiz.PHP.NonExecutableCode` sniff.
…eStatements

Implement the use of the new `Parentheses::hasOwner()` method in the `Generic.Formatting.DisallowMultipleStatements` sniff.
…ntAlignment

Implement the use of the new `Parentheses::hasOwner()` method in the `Generic.Formatting.MultipleStatementAlignment` sniff.
@mwgamble
Copy link

Or would you rather I do a back-merge of master to this PR and make the additional fixes and such as separate commits after that ?

Foxtrot merges should typically be avoided:

https://blog.developer.atlassian.com/stop-foxtrots-now/

@gsherwood
Copy link
Member

Thanks for reviewing all my comments. I've resolved all the conversations where we agree and left the others open for more discussion.

For such a big PR, only having 6 disagreements seems like a pretty good result to me :)

@gsherwood
Copy link
Member

Do you agree with this workflow ?

Yes, very happy with this.

@jrfnl
Copy link
Contributor Author

jrfnl commented Jul 25, 2019

@antograssiot @webimpress @photodude @klausi @TomasVotruba @dingo-d @GaryJones @umherirrender @djoos @weierophinney @louisl @Majkl578 @dereuromark @mbabker @lenaorobei @VasekPurchart @sirbrillig @legoktm @kukulich

Apologies for the group ping, but I would love to see your input in the still open discussion point regarding the naming of the utility classes.

These are the open discussions which would benefit from your input:

As maintainers of some of the largest public external standards for PHPCS you will likely be working with these classes, so your input on what you would find logical names will be invaluable and can help us take the most-balanced decision.

Thanks!

P.S.: If you've handed over the baton to someone else in the mean time or feel I've missed someone in the above list, please feel free to ping them and invite them to this discussion. No offense meant to anyone I forgot to ping.

@louisl
Copy link

louisl commented Jul 25, 2019

@jrfnl Good work and thanks for the notification.

@jrfnl
Copy link
Contributor Author

jrfnl commented Jul 30, 2019

@GaryJones @dingo-d @TomasVotruba @louisl @sirbrillig Thank you for your input so far!

@gsherwood I propose we leave the decisions till beginning of next week to allow some more people to add their voice to the discussion. I realize it's a holiday period in parts of the world, so keeping my fingers crossed.
I won't have time to do the rebase till next week at the earliest anyway ;-)

@jrfnl
Copy link
Contributor Author

jrfnl commented Aug 8, 2019

Unfortunately we haven't had more input since my last comment. All the same, the input we did receive was useful. Thanks everyone who contributed to the discussions!

So I think it's decision time. Here's the summary of my take-aways from the input received so far:

General note on appending Util to classes:

In the original setup all classes had this suffix. This was previously discussed in #2189 and it was decided at the time to remove the Util suffix from the classes (and move the classes to the Util/Sniffs subdirectory instead).

Regarding the Ortography class:

I haven't seen strong opinions either way, except for @gsherwood's and mine.

When I worked on the class, I originally had something like Punctuation, but as it also deals with the case of the first character of comments, text strings and construct names, that didn't seem to cover it.
I have to admit that as a non-native English-speaker, I was quite ecstatic to have found a more appropriate term 😆.

I do still feel that the methods in that class don't belong in a TextStrings class as they apply to more than just text string tokens.

If someone can come up with a more intuitive/less obscure name than Ortography, I'd be happy to change it, though I would still advocate keeping these methods in a separate class.

Conclusion: undecided.

Regarding the TextStrings class:

I still hold the opinion that as this class contains methods for use with T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING, T_INLINE_HTML, T_HEREDOC and T_NOWDOC tokens, while there is also the T_STRING token, which the methods in this class are definitely not targetting, naming the class Strings or StringUtils would cause more confusion rather than less.

I also agree with @TomasVotruba's point about generic class names which could easily turn into a code smell.

Conclusion: undecided.

Regarding the TokenIs class:

There are a number of good alternative name suggestions. I don't have a strong preference for any of them, but agree that all of them are better than the current name.

Current alternative suggestions to choose from:

  • TokenClarifier
  • TokenAnalyzer
  • TokenResolver

Conclusion: name will be changed, to what is to be decided.

Regarding the PassedParameters class:

Based on the feedback, I will add an additional Util class ArrayDeclarations which will (for now) just contain wrapper functions for the methods in the PassedParameters class with Parameter(s) in the method name replaced by Item(s), i.e:

  • PassedParameters::hasParameters() would be called ArrayDeclarations::hasItems();
  • PassedParameters::getParameters() would be called ArrayDeclarations::getItems();
  • PassedParameters::getParameter() would be called ArrayDeclarations::getItem();
  • PassedParameters::getParameterCount() would be called ArrayDeclarations::getItemCount();
  • PassedParameters::getDoubleArrowPosition() would be called ArrayDeclarations::getDoubleArrowPosition().

I'm proposing using ArrayDeclarations as the class name instead of just Arrays, as that could (again) be considered a code-smell.

I'm not keen to move the getDoubleArrowPosition() method as IIRC this method should also work for getting the double arrow position in PHP 7.1+ keyed lists.
I propose to add some unit tests to that effect and to update the method documentation to make it clearer that the method works for both arrays as well as lists.

Conclusion: an ArrayDeclarations wrapper class will be added.

Open questions:

  1. Should a similar wrapper class be added for lists ? Something like ListAssignments ?
  2. If both the ArrayDeclarations and ListAssignments wrapper classes are added, should the PassedParameters class then be renamed to FunctionCalls FunctionParameters ?

Wrap-up

Aside from the above, I think everything else is resolved.

I'd like to start the rebase ASAP to hopefully have a mergable PR by the middle of next week.

@gsherwood Will you do the honours and have the final say ?

@gsherwood
Copy link
Member

General note on appending Util to classes:

In the original setup all classes had this suffix. This was previously discussed in #2189 and it was decided at the time to remove the Util suffix from the classes (and move the classes to the Util/Sniffs subdirectory instead).

I'd be happy to consider this again. It would make naming these classes easier. See below.

Regarding the Ortography class:

I haven't seen strong opinions either way, except for @gsherwood's and mine.

The 3 developers who commented on the class all seemed to think that the methods could move to TextStrings. I think TextStrings should be renamed to StringUtil and the methods merged. One place to go looking for string-based utility functions. I think the name fits nicely with the other util classes.

I would set the class names as:

  • ArrayUtil
  • CommentUtil
  • ConditionUtil
  • ConstructNameUtil
  • FunctionDeclarationUtil
  • NamespaceUtil
  • ObjectDeclarationUtil
  • ParenthesesUtil (ParenthesisUtil would be technically more correct)
  • PassedParameterUtil (still not quite happy with this one)
  • StringUtil (merges TextString and Ortography)
  • TokenUtil (formerly TokenIs)
  • UseStatementUtil
  • VariableUtil

This is where my thinking is right now.

@jrfnl
Copy link
Contributor Author

jrfnl commented Aug 9, 2019

Argh.... seriously ? That would mean not just rebasing, but re-doing every single commit for a second time to bring back the Util in the names....

@gsherwood
Copy link
Member

That would mean not just rebasing, but re-doing every single commit for a second time to bring back to Util in the names....

Happy to leave this for 3.6.0 given the work and the fact discussion are still ongoing. I really need to get 3.5.0 out ASAP, but then I could provide more help over the next few months.

@mwgamble
Copy link

mwgamble commented Aug 9, 2019

I definitely think I'd prefer having Util as the suffix for most of these classnames. If they weren't there already, I'd probably alias them to have the Util suffix when importing them.

@jrfnl
Copy link
Contributor Author

jrfnl commented Aug 9, 2019

Happy to leave this for 3.6.0 given the work and the fact discussion are still ongoing.

I'm not as this being up in the air is, and has been for months, a blocker for several standards I work on.

I think we can all agree that this refactor is a good thing:

  • It fixes a sh*tload of bugs in PHPCS.
  • It makes sure all utility functions are actually unit tested.
  • It removes a lot of duplicate code.
  • And it adds a number of much needed additional utilities.

Asking me to rename a few more things still is fine. Asking me to rename every single thing a second time is not acceptable. That's just being callous with my time.
The last time it took every minute I could spare over a good three weeks to do that.

I could have decided straight off the bat to create this as a separate PHPCS utility repo which standards could pull in as a dependency and use.

Instead I've tried to be a good open source citizen and give back, discussed things first in issue #2189 and over time pulled this PR, but my patience is running out after ten months.

I really need to get 3.5.0 out ASAP, but then I could provide more help over the next few months.

I've been trying to coach this through now for 10 months, waiting for responses for weeks, sometimes months on end.

Sorry to say, but the history of this repo does not allow me to have much confidence in a statement like I could provide more help over the next few months, quite apart from it then still being a blocker for many more months.

I suggest we all sleep on it over the weekend and reconvene on Monday.

But to be fair, if the renaming all things stands, I will be pulling the PR and publishing this as a separate package.
PHPCS can then decide whether or not they want to add it as a dependency to fix all the bugs I fixed or muddle on.
For external standards, it shouldn't matter much either way.

@gsherwood
Copy link
Member

@jrfnl Sorry, I wasn't trying to push the PR back because I don't think it's amazing. I was just trying to take some pressure off you. I misread that - I'm sorry.

Asking me to rename a few more things still is fine. Asking me to rename every single thing a second time is not acceptable. That's just being callous with my time.
The last time it took every minute I could spare over a good three weeks to do that.

I was not aware of how long the process was to rename these classes. I'm not concerned about the commit history showing the rename, so I thought you were going to go in and commit a new change. If you're not bothered by the history, I can find some time this week to get that rename done, but wouldn't try and clean up the history. I'd probably do a (careful) string replace over the code base and fix things until it works.

But to be fair, if the renaming all things stands, I will be pulling the PR and publishing this as a separate package... For external standards, it shouldn't matter much either way.

It think would matter, especially if I made the name changes. They'd soon be incompatible, and would be even less compatible as time went on. I think it would be better for the PHP community if we can figure this out rather than forcing a split, but I understand if you want to create a new project instead.

I'm not trying to waste your time. I appreciate everything you've done for this project more than you know. I talk about you at work and home often because your contributions have kept this project - one that I am passionate about - healthy and growing while my life has gotten increasingly more complicated and busy. I've had some really great contributors help me out in the past, but none like you, and I don't mind saying this in public because I'm sure they would agree.

So whatever happens here, thank you.

@jrfnl
Copy link
Contributor Author

jrfnl commented Aug 10, 2019

They'd soon be incompatible, and would be even less compatible as time went on. I think it would be better for the PHP community if we can figure this out rather than forcing a split

@gsherwood I think you misunderstood part of my post.

I will be pulling the PR and publishing this as a separate package... For external standards, it shouldn't matter much either way.

What I meant by that is that I would publish the new and improved utility functions as an PHPCS external standard without sniffs.
That package could then be an additional dependency for external standards and things would (should) still work fine whether end-users use those standards via Composer or as stand-alone installs. People would just need to make sure the extra package is registered as an installed standard with PHPCS.

I was not talking about creating and maintaining a different fork of this repo.

The advantages of having the utility functions available as a separate package:

  • The utilities would be available for PHPCS 3.x+.
    External standards wouldn't need to raise their minimum PHPCS requirement.
  • Turn-around speed.
    I could just get on with it. I already have quite a few other classes and functions lying around which should be added to the Utility function suite, but I haven't added them here as I didn't want to muddy the waters for this PR/make reviewing more difficult with things being added all the time.
    And I fear that even if this PR would be merged, the turn-around time for adding additional utilities would still be just as slow as things have been for the past few years, so having a separate package would make these utilities available to external standards a lot faster.
  • I would still get rid of the maintenance burden of maintaining the duplicate code of these utility functions in a number of external standards and could start cleaning up those standards.

The disadvantages of it being a separate package:

  • The multitude of bugs in PHPCS itself would not get fixed (unless PHPCS decides to add the utility functions package as a dependency and implement the use of it).
  • I have quite a number of local branches with PSR-12 sniffs and other fixes ready to be pulled, but can't pull them to PHPCS as they need the utility functions/other changes from this PR (or would contain lots of duplicate code which would have to be removed at a later point or maintained in several places now, which I'm really not keen on).
    I'd need to think it over, but it's quite likely that I would publish them in a separate external PSR-12 standard instead of pulling them here if the utility methods would become a separate package.

So, in other words, advantages for everyone, except PHPCS itself.

It may take end-users a little while to find the new standards and such, but I don't expect that will take too long, especially once PSR-12 is approved and published and they find the standard here to be far from complete.

I was just trying to take some pressure off you.

Sorry, no. Taking pressure off me, would have been reviewing & merging this as soon as the 3.4.2 release was done as we previously discussed.
This refactor was supposed to go into 3.5.0 early so any bugs which may exist would be caught before the release, not go in last minute as you now suddenly "really need to get 3.5.0 out ASAP".

If you're not bothered by the history, I can find some time this week to get that rename done, but wouldn't try and clean up the history. I'd probably do a (careful) string replace over the code base and fix things until it works.

After all the effort which has gone into this PR to have everything as clean atomic commits, that, again, is just callous.

I will step away from keyboard now and not take a final decision until after the weekend, but I stand by what I said earlier: If the request to rename everything stands, I will pull the PR.
Enough is enough.

@gsherwood
Copy link
Member

@jrfnl and I have spoken privately about this PR over the last couple of weeks and we've decided that the best way forward is to release these util classes as a separate package. This will immediately provide new tools for sniff developers to use without needing to consider the impact on the core of PHPCS itself as they evolve.

@mwgamble
Copy link

@jrfnl and I have spoken privately about this PR over the last couple of weeks and we've decided that the best way forward is to release these util classes as a separate package. This will immediately provide new tools for sniff developers to use without needing to consider the impact on the core of PHPCS itself as they evolve.

This is a very disappointing outcome IMO. I thought this was also about cleaning up many sniffs that desperately needed refactoring, in addition to providing important utility functionality for general use.

@gsherwood gsherwood removed this from the 3.5.0 milestone Aug 27, 2019
@dingo-d
Copy link

dingo-d commented Aug 27, 2019

I'm also saddened by this decision. This is a PR that is doing so much more than just a simple cleanup, it helps us when working on external standards (while working on the standards there are couple of utility functions that I identified that would most likely be covered in this PR). Having a separate package to maintain can just cause harm in the long run imo.

@mwgamble
Copy link

So is this PR just dead in the water now? Months of hard work left to waste because of a naming dispute?

@jrfnl
Copy link
Contributor Author

jrfnl commented Oct 16, 2019

@mwgamble I'll be publishing the functions elsewhere soon. They will be available for external standards supporting PHPCS 2.6.0+ up to master.
If you like, keep an eye on: https://github.com/PHPCSStandards/PHPCSUtils

@jrfnl
Copy link
Contributor Author

jrfnl commented Jan 30, 2020

FYI: I've tagged the first alpha release for PHPCSUtils. You will find nearly all the new utilities from this PR there plus many more additional utility functions, token collections as static properties, abstract sniff classes and fixers for use with PHPCS.

On top of that a lot of the PHPCS native utility functions are also available via PHPCSUtils in a BackCompat layer, allowing external standards to use the latest version of the PHPCS native utilities even while still supporting older PHPCS versions.

All the utilities published are compatible with PHPCS 2.6.0 - master.

There are two classes which I've held held back for now as I deem them to need further improvement (Comments and ConstructNames).

I'm fully aware that the readme is very sparse at the moment. I'm working on that (and many more things) and it will be filled in before the 1.0.0 release. I also intend to launch a website with the documentation of all the utilities at the same time.

https://github.com/PHPCSStandards/PHPCSUtils
https://packagist.org/packages/phpcsstandards/phpcsutils

The two new sniffs which were included in this PR DisallowLongListSyntax and DisallowShortListSyntax have been published in PHPCSExtra, for which I've also tagged the first alpha release.
This release includes some 12 new sniff (so far), most of which use utilities from PHPCSUtils.

https://github.com/PHPCSStandards/PHPCSExtra
https://packagist.org/packages/phpcsstandards/phpcsextra

Both more utility methods, as well as more new sniffs can be expected to be published in these packages in the (near) future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants