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

request for additional configuration option for native_function_invocation #2739

Closed
SignpostMarv opened this issue May 1, 2017 · 15 comments
Closed

Comments

@SignpostMarv
Copy link

I've finally been getting around to using php-cs-fixer 2.x (not waiting around for #2553 to be resolved), and I ended up experimenting with it on vimeo/psalm#152, but some of the tests produce a rather verbose diff file ([nearly 7000 lines)(https://gist.github.com/SignpostMarv/840cac5070e7b5e840588b8e51903c18#file-native_function_invocation-only-diff)).

The argument behind native_function_invocation is to speed up resolving, and most things that improve performance are good things, but having such large diffs in pull requests is probably a bad idea (for several reasons that I won't go into at the moment).

So a thought occurred to me; use function strtolower; produces the same opcodes as \strtolower(); see 3v4l.org: backslash vs. 3v4l.org: use function.

Since use function has been available since 5.6, would it be possible to have a configuration option added to native_function_invocation to enable smaller diffs to be generated with the same performance improvement? i.e.:

$rules = [
    'native_function_invocation' => [
        'import' => true, // defaults to false to preserve existing behaviour
    ],
];

for convenience/those not wanting to click on 3val.org links:

<?php

namespace Foo;

use function strtolower;

strtolower('Foo');
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /in/IUsaC
function name:  (null)
number of ops:  4
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   7     0  E >   INIT_FCALL                                               'strtolower'
         1        SEND_VAL                                                 'Foo'
         2        DO_ICALL                                                 
         3      > RETURN                                                   1
<?php

namespace Foo;

\strtolower('bar');
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /in/gvU56
function name:  (null)
number of ops:  4
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   INIT_FCALL                                               'strtolower'
         1        SEND_VAL                                                 'bar'
         2        DO_ICALL                                                 
         3      > RETURN                                                   1
@julienfalque
Copy link
Member

Sounds like a good idea to me 👍

@keradus
Copy link
Member

keradus commented May 1, 2017

It was already discussed once with author of rule - @localheinz - as nice config option to have, but was postponed to undefined future. ref #2462

feel free to open a PR proposal.

@SignpostMarv
Copy link
Author

Is there an existing rule that'd be a good starting point to look at to trigger the insertion of use statements ?

SignpostMarv added a commit to SignpostMarv/psalm that referenced this issue May 6, 2017
@keradus
Copy link
Member

keradus commented May 6, 2017

Instead of having this rule configurable, I would rather make new generic rule (that is executed after this, existing one) that would import all used classes from global namespaces (configurable to work with classes/functions/traits)

+use Foo\Bar;
 
-$x = new \Foo\Bar();
+$x = new Bar();

@keradus
Copy link
Member

keradus commented May 6, 2017

@SignpostMarv I can't remind any

@SignpostMarv
Copy link
Author

import_absolute_references ?

@keradus
Copy link
Member

keradus commented May 8, 2017

sth like this, name could always be adjusted during review, if one would figure out better name (now, I cant ;) )

@SignpostMarv
Copy link
Author

I was poking around the php-cs-fixer source last night, thinking "no_absolute_references" under PhpCsFixer\Fixer\Import might be a good fit ?

Also found something that may or may not be a bit of a problem - getPriority(); we'd want this to run after NativeFunctionInvocationFixer, but before NoUnusedImportsFixer & OrderedImportsFixer, might need to change things ?

@keradus
Copy link
Member

keradus commented May 10, 2017

as it's suppose to import things, import namespace is ok
priority, as long as you know what you want to achieve, won't be a problem

@SignpostMarv
Copy link
Author

wouldn't changing the priority on a bunch of other fixers be a problem for others ?

@keradus
Copy link
Member

keradus commented May 10, 2017

priorities are to set relations between fixers, like fixer A has to be executed before fixer B.
as long as integration tests will be fine after your changes, everything is fine

@SignpostMarv
Copy link
Author

poking around in the code a bit more today, so I can probably figure out how to add new use foo; statements, but I'm not sure how to identify the appropriate insertion point- I'm looking at what OrderedImportsFixer does with Tokens::fromCode() & $tokens->overrideRange()

presumably we want to insert tokens on the line after one of

  • "<?" or "<?php" (first available)
  • namespace Foo; (used instead of opening tag position)
  • use Foo;

would appreciate help if I'm looking at the wrong example to figure things out from :D

@keradus
Copy link
Member

keradus commented May 17, 2017

I would suggest to do it step by step.
First step: create fixer that is not doing what you wanna achieve :D at least not fully.
GeneralImportAddFixer, that will receive configuration what to import and it will add imports for it.

So here, without detecting what to import. After it is done, you could reuse it in new fixer like NoFQCNWhenImportedFixer, that will detect what to import, ask first fixer to import it, and drop FQCN from usage.` But let us come back to this fixer after first part is ready and working.

I would ignore:

  • multi-namespaced files (we already have method for it: Tokens->findKinds... and count it)
  • non monolitic files (=multiple <?php) (we already have method for it as well: isMonolitic)
    like we already do it some fixers.

then like you said, search for last existing imports, if it's not there go for namespace, if it's not there go for opening tag and skip following declare/docblock

You have the right place to add things. Now you need to build tokens for things you want to import and Tokens->insertAt them (no need to override).
You could convert string use Foo\Bar; via fromCode, but I would rather suggest to build Token items manually (parsing is fast, and we do know what string we want to achieve so we could prepare the tokens manually)

Don't be shy to reach me over gitter if you have any questions !

SignpostMarv added a commit to SignpostMarv/psalm that referenced this issue May 26, 2017
muglug pushed a commit to vimeo/psalm that referenced this issue May 26, 2017
* swapping phpcs for php-cs-fixer

* workaround for php-cs-fixer treating parenthesis following echo as the function call variant

* amending rules

* blank_line_before_return

* majority of files pass with these disabled, could remove later

* combine_consecutive_unsets

* concat_space

* placeholder for if vimeo/psalm ever goes php:^7.0

* function_to_constant

* disabling include

* linebreak_after_opening_tag, lowercase_cast, magic_constant_casing

* mb_str_functions disabled

* method_separation

* native_function_casing

* native_function_invocations

* new_with_braces disabled to match usage

* no_alias_functions

* no_blank_lines_after_class_opening

* no_blank_lines_after_phpdoc

* no_blank_lines_before_namespace

* no_empty_comment

* no_empty_phpdoc

* no_empty_statement

* no_extra_consecutive_blank_lines

* no_leading_import_slash to discuss

* no_leading_namespace_whitespace

* no_mixed_echo_print

* no_multiline_whitespace_around_double_arrow

* no_multiline_whitespace_before_semicolons

* no_php4_constructor

* no_short_bool_cast

* no_short_echo_tag

* no_singleline_whitespace_before_semicolons

* no_spaces_around_offset

* no_trailing_comma_in_list_call

* no_trailing_comma_in_singleline_array

* no_unneeded_control_parentheses to discuss

* no_unreachable_default_argument_value

* no_unused_imports to discuss

* no_useless_else to discuss

* no_useless_return

* no_whitespace_before_comma_in_array

* no_whitespace_in_blank_line

* non_printable_character

* normalize_index_brace

* ordered_class_elements to discuss

* ordered_imports to discss

* php_unit_construct

* php_unit_dedicate_assert

* php_unit_fqcn_annotation

* php_unit_strict to discuss

* php_unit_test_class_requires_covers to discuss

* phpdoc_add_missing_param_annotation

* phpdoc_align to discuss

* phpdoc_annotation_without_dot to discuss

* phpdoc_indent to discuss

* phpdoc_inline_tag

* phpdoc_no_access

* phpdoc_no_alias_tag

* phpdoc_no_empty_return

* phpdoc_no_package

* phpdoc_no_useless_inheritdoc

* phpdoc_order to discuss

* phpdoc_return_self_reference

* phpdoc_scalar to discuss

* phpdoc_separation to discuss

* phpdoc_single_line_var_spacing

* phpdoc_summary to discuss

* phpdoc_to_comment to discuss

* phpdoc_trim to discuss

* phpdoc_types

* phpdoc_var_without_name

* pow_to_exponentiation

* pre_increment to discuss

* protected_to_private

* psr0 turned off

* psr4 turned on

* random_api_migration

* return_type_declaration to discuss

* self_accessor to discuss

* semicolon_after_instruction

* short_scalar_cast

* silenced_deprecation_error turned off

* simplified_null_return to discuss

* single_quote

* space_after_semicolon

* standardize_not_equals

* strict_comparison to discuss

* strict_param to discuss

* ternary_operator_spaces

* ternary_to_null_coalescing should be set to true if vimeo/psalm ever goes php:^7.0

* trailing_comma_in_multiline_array to discuss

* trim_array_spaces

* unary_operator_spaces

* whitespace_after_comma_in_array to discuss

* multi-version scrutinizer to match travis

* binary_operator_space

* not the best solution, but it works to exclude the call map from php-cs-fixer

* reducing verbosity of config where defaults were used

* dry run php-cs-fixer as part of tests

* disabling rule pending PHP-CS-Fixer/PHP-CS-Fixer#2739

* enabling no_unused_imports

* enabling ordered_imports

* ignoring user-defined .php_cs

* using $TRAVIS_COMMIT_RANGE to only test modified files

* enabling no_leading_import_slash

* conditionally testing everything

* filter output then perform exact match

* restoring phpcs via partial cherry pick of f65c618
@jaydiablo
Copy link
Contributor

This would really be nice as an option to the native_function_invocation fixer (which I think is what has been proposed). Used with the scope option set to namespaced may be a requirement, as PHP doesn't like the use function X statements when the code isn't namespaced.

https://3v4l.org/oicuF

Warning: The use statement with non-compound name 'strtolower' has no effect in /in/oicuF on line 3

SpacePossum added a commit that referenced this issue Oct 11, 2019
This PR was squashed before being merged into the 2.16-dev branch (closes #4355).

Discussion
----------

GlobalNamespaceImportFixer - Introduction

* closes #1309
* refs #2166 (this pr does not differientiate between native/non-native elements, only between global/non-global)
* closes #2739
* closes #4347

---

The fixer can import global classes/functions/constants:

Input:

```php
<?php

namespace Foo;

if (\count($x)) {
    /** @var \DateTimeImmutable $d */
    $d = new \DateTimeImmutable();
    $p = \M_PI;
}
```

Output:

```php
<?php

namespace Foo;
use DateTimeImmutable;
use function count;
use const M_PI;

if (count($x)) {
    /** @var DateTimeImmutable $d */
    $d = new DateTimeImmutable();
    $p = M_PI;
}
```

Global functions/constants without leading `\` are not imported. The slash can be added by `Native(Constant|Function)InvocationFixer` before.

The fixer can also do the reverse fix, adding the backslash to imported global classes/functions/constants. But the fixer does not remove the imports, this can be done by `NoUnusedImportsFixer`.

Commits
-------

41fe1f4 GlobalNamespaceImportFixer - Introduction
@SpacePossum
Copy link
Contributor

done in #4355

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

No branches or pull requests

5 participants