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

PreloadExplicitClassSymbolsFixer - introduction #4891

Closed
wants to merge 11 commits into from

Conversation

Nyholm
Copy link
Contributor

@Nyholm Nyholm commented Mar 20, 2020

The idea is to help a preloader to find classes that are used by adding a bunch of class_exists in the file.

A typical preloader uses reflection. See Symfony.

This PR is a draft to give an idea what I want to do. Please review first commit to get the idea without all complex logic.

TODO

  • Add a better way to detect what classes we don't need to preload, ie classes that is found by reflection
  • Better find and include classes we do need to preload
  • Add a bunch of more tests

@julienfalque
Copy link
Member

This looks like a good idea but I don't have all the background information required to understand it.

The idea is to help a preloader to find classes that are used by adding a bunch of class_exists in the file.

Can you elaborate a bit please?

@Nyholm
Copy link
Contributor Author

Nyholm commented Mar 21, 2020

Look at the Symfony preloader. It reads all public functions for their arguments and return types. It makes sure to preload them.

However, Preloaders can not find class App\Foo in the code below:

use App\Foo;

class Case003
{
    private $foo;
    public function __construct()
    {
        $this->foo = new Foo();
    }
}

That is why you need to help the preloader by loading the class Foo in runtime. That could easily be done by adding class_exists(Foo::class).

See this PR as an example for such work: symfony/symfony#36103

@Nyholm
Copy link
Contributor Author

Nyholm commented Mar 21, 2020

This is my first fixer.
It was a huge relief when I discovered all methods on Tokens and the FunctionAnalyzer.

I would appreciate some help with the FIXME comments. Is there an easy/common way to solve these issues or will I have to try to invent something myself?

use App\Foo;

class_exists('App\Foo');
class_exists(Foo::class);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not a nice solution. It will duplicate the class_exists(Foo::class); for the App\Foo class. However, it is soo much more easier.

Allowing this would avoid parsing use statements and class alias to figure out that App\Foo is already "preloaded"

Copy link
Member

Choose a reason for hiding this comment

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

(...) and class alias (...)

I don't see class alias here in this test case, but if you need to handle them, maybe use https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.16/src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.php ?

@Nyholm Nyholm force-pushed the preload branch 3 times, most recently from 6b1b0f0 to fd3ac34 Compare March 21, 2020 14:29
public function getDefinition()
{
return new FixerDefinition(
'Adds extra `class_exists` before a class to help opcache.preload to discover always-needed symbols.',
Copy link
Member

Choose a reason for hiding this comment

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

Nice idea to make this as a fixer!

Please provide a $description parameter with the reasoning, especially:

  • why the rule is worth to use?
  • what classes we don't need to preload, ie classes that is found by reflection and why

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a desciption

$class = $tokens[$tokens->getPrevMeaningfulToken($i)]->getContent();
$classes[] = $class;
} elseif ($token->isGivenKind(T_OBJECT_OPERATOR)) {
// FIXME Better check if function call to avoid false positive like "$this->bar = 2;"
Copy link
Member

Choose a reason for hiding this comment

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

@Nyholm , are those 3 FIXME cases covered by tests already? if not, maybe we can add a test case for each, so we can easier understand what is example code snippet you struggle with and expected outcome ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are. They are the failing tests.
I'll work with them now to see if I can make sure they are green

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tests are green. Locally at least. =)

@Nyholm Nyholm marked this pull request as ready for review April 4, 2020 18:02
.travis.yml Outdated Show resolved Hide resolved
@SpacePossum SpacePossum changed the title Support for preload PreloadExplicitClassSymbolsFixer - introduction Apr 5, 2020
new VersionSpecification(70300)
),
],
'A script for opcache.preload should classes that are used by the average '
Copy link
Member

Choose a reason for hiding this comment

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

A script for opcache.preload should classes that are used by the average HTTP request.

should classes? sorry, i can't understand this sentence

],
'A script for opcache.preload should classes that are used by the average '
.'HTTP request. A good preloading script uses reflection to load the '
.'hot class and all classes it depends on. Reflection cannot always find '
Copy link
Member

Choose a reason for hiding this comment

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

what is hot class?
there is only ONE hot class, or many of them (classes)?

'A script for opcache.preload should classes that are used by the average '
.'HTTP request. A good preloading script uses reflection to load the '
.'hot class and all classes it depends on. Reflection cannot always find '
.'all dependencies. Example: Classes used in the constructor or dependent '
Copy link
Member

@keradus keradus Apr 7, 2020

Choose a reason for hiding this comment

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

Reflection cannot always find all dependencies

why?

(sorry for dummy questions, I am unaware about the idea and description here is not helping me to get what exactly should be added as outcome of this rule and why, and what not as it's automatically picked up by some preloading script)

}

// TODO parse public properties
// TODO Get the class' interfaces and parent
Copy link
Member

Choose a reason for hiding this comment

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

marker
(so we don't merge before it's solved)

Copy link
Member

Choose a reason for hiding this comment

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

@Nyholm , would you like to finalize this PR?

$newTokens[] = new Token([CT::T_CLASS_CONSTANT, 'class']);
$newTokens[] = new Token(')');
$newTokens[] = new Token(';');
$newTokens[] = new Token([T_WHITESPACE, "\n"]);
Copy link
Member

@keradus keradus Apr 7, 2020

Choose a reason for hiding this comment

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

Suggested change
$newTokens[] = new Token([T_WHITESPACE, "\n"]);
$newTokens[] = new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding()]);

(you need to implement WhitespacesAwareFixerInterface also)

@Wirone Wirone added the status/to verify issue needs to be confirmed or analysed to continue label Jun 6, 2023
@Wirone
Copy link
Member

Wirone commented Jul 13, 2023

Does anyone know if this PR is relevant in 2023 and PHP 8.2 (at the brink of 8.3)? @Nyholm, maybe @nicolas-grekas? 🤔

@Wirone Wirone added the status/input required For the issue to be confirmed or the PR to be process; additional input of the author is required label Jul 13, 2023
@Nyholm
Copy link
Contributor Author

Nyholm commented Jul 13, 2023

I was thinking of this pr the other day. Let’s close it.

If someone wants to pick it up, then feel free to open.

@Nyholm Nyholm closed this Jul 13, 2023
@Wirone Wirone removed status/to verify issue needs to be confirmed or analysed to continue status/input required For the issue to be confirmed or the PR to be process; additional input of the author is required labels Jul 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants