Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add SpecifyArgSeparatorFixer to make sure http_build_query is properly used #18

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

Merged
OndraM merged 2 commits into alma-oss:master from OndraM:feature/arg-separator
May 14, 2018

Conversation

@OndraM
Copy link
Contributor

@OndraM OndraM commented May 7, 2018

Fixes #17 .

To be honest, this is the first Fixer I've ever written. So any suggestions especially regarding usage of php-cs-fixer internals (the tokens stuff etc.) are welcome. Also I may overlook some simpler way how to accomplish this, so I'm open for suggestions.

Cc @TomasVotruba - I would be grateful to hear some feedback on the Fixer from you as well ❤️ :-)

TomasVotruba reacted with thumbs up emoji
@OndraM OndraM added the enhancement New feature or request label May 7, 2018
Copy link

@TomasVotruba TomasVotruba left a comment

Choose a reason for hiding this comment

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

Very nice work on first Fixer. I wish mine was to simple and easy to understand :D

Since you don't use pure PHP CS Fixer, I have one more tip: I do use ECS Tester package, to simulate 1:1 ECS behavior (less WTF, as it's the same as final code)

},
"autoload-dev": {
"psr-4": {
"Lmc\\CodingStandard\\": "tests/"
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

Why not convetion "Lmc\\CodingStandard\\Tests\\": "tests/"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We use the same namespace for Class and its corresponding test.

MortalFlesh reacted with thumbs up emoji
Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

I know :D but why not convention?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AFAIK both of the approaches are widely used, so I don't consider any of them as "general convention".

And why it should be in standalone namespace in the first place? I don't see a reason for this.

The same namespace means you don't need to import the class under test namespace, also it makes sense to me to have class and its tests in the same "module" (namespace), because they belongs to each another.

But its still more about personal preference...

Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

Similar unconvention as putting src and tests in the same directory, causes in php cs fixer only troubles. 80 % of php-scoper setup took me only this to solve.

It's really painful to work with such packages, where maintainer don't see behing the package common use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I don't understand :). This is the way we use it for years and never heard of single issue. And it is not any "hack", just usual setting of Composer autoloading. But yeah, I can imagine this could cause trouble when developing some low-level stuff.

BTW src/ and tests/ in the same (root) directory are used almost everywhere except packages which are then splited to sub-packages (like Symfony). I find most inconvenient when source is not in standalone directory and mixed with Tests directory on the same level. Setting code-coverage or phpcs etc. blacklist/whitelist is nightmare in this case.

Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

Again, open source code is very different to closed one.

Copy link
Contributor Author

@OndraM OndraM May 11, 2018
edited
Loading

Choose a reason for hiding this comment

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

True, but I don't think the difference is in the way autoloading is set up :). But this seems like a topic to pub/meetup, so let's don't forget it next time we meet :-D . I'd like to hear about the stuff you must dealt with :).

TomasVotruba reacted with thumbs up emoji
. '`$arg_separator` in third parameter of the function.',
null,
null,
'Risky when other than default "&" argument separator should be used in query strings.'
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

I'd expect this line rather (even) at isRisky() method

&& ($tokens[$previousIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)
|| $tokens[$previousIndex]->isGivenKind(T_FUNCTION))) {
return;
}
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

I'd add all this to "shouldSkip()" method, it's not relevant to fixer process at all

OndraM reacted with thumbs up emoji

public function getName(): string
{
return mb_strtolower(self::class);
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

Why did you choose this new approach?

I'd use one of standards: php-cs-fixer or ECS. How could you copy this class to easy-coding-standard.yml from CLI output to skip it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"The name must be all lowercase and without any spaces." - this is in phpDoc.
But I saw you use class name. So I combined them both :-D. What is the best solution? Where is this name being used?

ECS is depending just on the class name FQCN, or it somehow works with this?

$startBraceIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
if ($startBraceIndex === null) {
return;
}
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

Why is this here? Should be above the fix funciton

Copy link
Contributor Author

Choose a reason for hiding this comment

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

getNextTokenOfKind() can by annotation return null as well. So to adhere type safety, this case must be somehow handled.


continue;
}
}
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

I'd be happy to check after that, there is lot of outsourcable code now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:-D I was today looking for something like this like an half an hour and did not find it. So this makes part of the code unnecessary, I will refactor it with use of the ArgumentsAnalyzer. Thanks!

$thirdParamToken = $tokens[$thirdParamTokenIndex];

if ($thirdParamToken->isGivenKind(T_STRING) && $thirdParamToken->getContent() === 'null') {
$tokens->offsetSet($thirdParamTokenIndex, $ampToken);
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

This section very difficult to read. What does it do?

I'd expect it to be bellow, with other "modifying" code

$tokensToInsert[] = new Token([T_STRING, 'null']);
}

// Add third parameter (arg separator)
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

Real code might be also helfpul while reading this, e.g.:

// Add third parameter (arg separator): ", &")

I always add it because it's much more readable than custom Tokens

$fixer->fix($fileInfo, $tokens);

$this->assertSame(
file_get_contents(__DIR__ . '/Fixtures/' . $expectedOutputFile),
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

asssertStringEqualsFile() or how its called

return [
'Correct file should not be changed' => ['Correct.txt', 'Correct.txt'],
'Wrong file should be fixed' => ['Wrong.txt', 'Fixed.txt'],
];
Copy link

@TomasVotruba TomasVotruba May 7, 2018

Choose a reason for hiding this comment

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

While using dataProvider, it makes sense to use one file per case.
Imagine you have bug in Wrong.txt on line 25 - which one of these cases it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@TomasVotruba True, but there is overhead and is also harder to see what is covered and what not. Maybe rather write inline PHP for each case (as some fixer tests do).

You will see which line of code is broken by phpunit's diff, right?

Copy link

@TomasVotruba TomasVotruba May 10, 2018

Choose a reason for hiding this comment

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

Well, how do you run broken code only?

Copy link
Contributor

@MortalFlesh MortalFlesh May 11, 2018

Choose a reason for hiding this comment

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

You dont (I guess).. but why would you run just part of tests?
If you are developing something new, you can run single test case or I think even one perticular provider value test case in PHPStorm..

Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

I do that too, but how to run specific one line in this single file?

@OndraM OndraM force-pushed the feature/arg-separator branch from 8f1eca0 to bfbdc36 Compare May 7, 2018 21:02
- travis_retry composer update --no-interaction

script:
- composer all
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

why not to encapsulate those commands into all script?

Copy link
Contributor Author

@OndraM OndraM May 10, 2018
edited
Loading

Choose a reason for hiding this comment

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

I need phpunit with specific settings for CI (to generate clover). But I may do something like "all-ci" or so.

MortalFlesh reacted with thumbs up emoji
"require-dev": {
"j13k/yaml-lint": "^1.1",
"mhujer/yaml-sort-checker": "^1.2"
"mhujer/yaml-sort-checker": "^1.2",
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

Did you consider using lmc/coding-standards here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why :)? This is the package itself 🙃

foreach ($tokens as $index => $token) {
if ($token->isGivenKind(T_STRING) && $token->getContent() === 'http_build_query') {
if ($this->isNotFunctionCall($tokens, $index)) {
return;
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

Are you sure with return? IMHO this should be just continue, or am I wrong?

Imagine code

function foo($args) {
 $this->addCallback('http_build_query'); // this is not a function call
 return http_build_query($args); // this is a function call, but isn't it skipped due previous `return`?
}

Copy link
Contributor Author

@OndraM OndraM May 10, 2018
edited
Loading

Choose a reason for hiding this comment

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

Nice catch, fixed. 👍

Tests actually hide this, because the case with definition of function http_build_query were the last, so the bug did not show :). So I rearranged them.

}

$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startBraceIndex);
$ampToken = new Token([T_STRING, "'&'"]);
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

Well, would this Token find usage of http_build_query(..., "&");? You explicitely awaits it is in single quotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Priority is now set to -1, ie. it will run after SingleQuoteFixer.


$commaIndexes = [];

for ($index = $startBraceIndex + 1; $index < $endBraceIndex; ++$index) {
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

Just saying.. but this looks very confusing to me (+ 1, ++$index,...).

$token = $tokens[$index];

// Parameter surrounded by brace, skip to its end
if ($token->equals('(')) {
Copy link
Contributor

@MortalFlesh MortalFlesh May 9, 2018

Choose a reason for hiding this comment

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

mm.. I guess following code is a nightmare for Fixer author, but have you considered something like this?

http_build_query([], null, ('&'));

Copy link
Contributor Author

OndraM commented May 10, 2018

Changes done & refactored to use ArgumentsAnalyzer. cc @TomasVotruba @MortalFlesh et al. 🙏

Copy link

TomasVotruba commented May 10, 2018
edited
Loading

In which commit did you add the ArgumentAnalyzer? The commit messages are not very useful :(

/**
* Return false if this is function import or function definition (and not function call)
*/
private function isNotFunctionCall(Tokens $tokens, int $index): bool
Copy link
Contributor

@MortalFlesh MortalFlesh May 11, 2018

Choose a reason for hiding this comment

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

I'm not fan of negative bool methods or variables, it is confusing to use/read .. I'd prefer function isFunctionCall()
and then if (!$this->isFunctionCall())..

TomasVotruba reacted with thumbs up emoji
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Neither am I, but this function in fact doesn't prove it is function call when you just negate the logic - there are IMHO many edge cases. What it can is just to detect the main scenarios when it is not a function call :). So from my POV, the other name would be inaccurate and misleading :).
🤷

Copy link
Contributor

@MortalFlesh MortalFlesh May 11, 2018

Choose a reason for hiding this comment

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

Well.. 🤔
Ok in this case I think it might be ok..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MortalFlesh I thought about it again and changed it in the end... The objection still applies, but maybe its less harm like this 🤷‍♂️ .


private function getThirdArgumentInfo(
Tokens $tokens,
$openParenthesisIndex,
Copy link
Contributor

@MortalFlesh MortalFlesh May 11, 2018

Choose a reason for hiding this comment

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

Why there is no type?

TomasVotruba and OndraM reacted with thumbs up emoji
Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

How can this pass the CI?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😱

Copy link
Contributor Author

@OndraM OndraM May 11, 2018
edited
Loading

Choose a reason for hiding this comment

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

Fixed.

@TomasVotruba It seems phpdoc_add_missing_param_annotation (aka PhpdocAddMissingParamAnnotationFixer) does not work as I expected :-(. It only works if there is already at least something in phpdoc.

Do you know some check which takes care of this?

$argumentsAnalyzer = new ArgumentsAnalyzer();

$arguments = $argumentsAnalyzer->getArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex);
$argumentIndex = array_slice($arguments, 2, 1, true);
Copy link
Contributor

@MortalFlesh MortalFlesh May 11, 2018

Choose a reason for hiding this comment

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

Well I would name it rather $argument, instead of $argumentIndex, which I'd expect holds just an index of the argument.
And maybe name it explicitely $thirdArgument..

Copy link

@TomasVotruba TomasVotruba May 11, 2018
edited
Loading

Choose a reason for hiding this comment

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

So is this int (index/position) or some array/object ($argument) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The content is [startIndex => endIndex]. IMO argument is still misleading - it contains just the indexes, not the argument itself 🤔

Copy link

@TomasVotruba TomasVotruba May 11, 2018

Choose a reason for hiding this comment

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

I name these BlockInfo

Copy link
Contributor Author

OndraM commented May 11, 2018
edited
Loading

@TomasVotruba

In which commit did you add the ArgumentAnalyzer? The commit messages are not very useful :(

You can easily go to Files changed > Changes from: > Select "Since last review" or something like this (it is selected by default) to see what has changed.

We don't write new commit messages when you only adjust things in response to code-review feedback, because commit messages should be about behavior change in the code, to make the history help with long term maintainability of the code (it helps a lot when using git blame etc.). There already is commit about adding the fixer, so unless any new commit in this PR will add/change some other behavior, the changes are just associated to the original commit via fixup and will be rebased and squashed before merge.

Interesting article about this topic: https://arialdomartini.wordpress.com/2012/09/03/pre-emptive-commit-comments/ (especially "Tell me what the software does (not what you did)" part) and also of course https://filip-prochazka.com/blog/git-fixup about fixup commits.

MortalFlesh reacted with thumbs up emoji

Copy link

It's just uncommon and confusing in open source, that's all.

@OndraM OndraM force-pushed the feature/arg-separator branch from 77b31af to c834dd8 Compare May 14, 2018 15:02
@OndraM OndraM merged commit ebd6aec into alma-oss:master May 14, 2018
Copy link
Contributor Author

OndraM commented May 14, 2018

Merged, thanks @TomasVotruba @MortalFlesh a lot for your great feedback! 🥇

TomasVotruba and MortalFlesh reacted with thumbs up emoji

@OndraM OndraM deleted the feature/arg-separator branch May 14, 2018 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@MortalFlesh MortalFlesh MortalFlesh approved these changes

@kubasimon kubasimon Awaiting requested review from kubasimon

@jirinovak jirinovak Awaiting requested review from jirinovak

@legendik legendik Awaiting requested review from legendik

+1 more reviewer

@TomasVotruba TomasVotruba TomasVotruba left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Labels

enhancement New feature or request

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Make sure http_build_query is used with third param

AltStyle によって変換されたページ (->オリジナル) /