Skip to content

PHPStan: Add extension for typing Registry::call()#892

Merged
jtojnar merged 2 commits intomasterfrom
wip/jtojnar/phpstan-registry-call
Jun 27, 2025
Merged

PHPStan: Add extension for typing Registry::call()#892
jtojnar merged 2 commits intomasterfrom
wip/jtojnar/phpstan-registry-call

Conversation

@jtojnar
Copy link
Member

@jtojnar jtojnar commented Sep 30, 2024

Registry::call() is widely used in SimplePie to allow extensibility. Unfortunately, the pattern hides the actual method from PHPStan so it cannot determine the return value type, and there is no annotation we can add to make it work. , This results in an inadequate analysis.

Thankfully, most cases allow us to determine the method statically so we can create a PHPStan extension doing just that.

The extension might be useful for SimplePie consumers so let’s expose it in composer.json.

We may want to add more type checking extensions in the future.

@jtojnar jtojnar force-pushed the wip/jtojnar/phpstan-registry-call branch 2 times, most recently from 7f4cdcb to d1d2961 Compare September 30, 2024 00:52
@jtojnar jtojnar mentioned this pull request Sep 30, 2024
@jtojnar jtojnar requested a review from Art4 September 30, 2024 00:55
@jtojnar jtojnar marked this pull request as ready for review September 30, 2024 00:55
@jtojnar jtojnar force-pushed the wip/jtojnar/phpstan-registry-call branch from d1d2961 to aa72efb Compare September 30, 2024 01:04
@Art4
Copy link
Contributor

Art4 commented Sep 30, 2024

I would recommend to move this extension class inside the tests folder, maybe tests/Fixtures/PHPStan and namespace SimplePie\Tests\Fixtures\PHPStan.

@Art4 Art4 added this to the 1.9.0 milestone Sep 30, 2024
@jtojnar
Copy link
Member Author

jtojnar commented Sep 30, 2024

I do not think it is really a fixture. I am also considering adding more maintenance scripts (e.g. Rector rules for automatic migration to SimplePie 2).

@jtojnar
Copy link
Member Author

jtojnar commented Sep 30, 2024

Actually, looking at composer/pcre, I guess it should go to src so that consumers can make use of it too.

@jtojnar jtojnar force-pushed the wip/jtojnar/phpstan-registry-call branch 7 times, most recently from 781b40a to 2943662 Compare September 30, 2024 19:35
@jtojnar
Copy link
Member Author

jtojnar commented Sep 30, 2024

Moved the file and added a extension.neon (listing it in README and composer.json).

@jtojnar jtojnar force-pushed the wip/jtojnar/phpstan-registry-call branch from 2943662 to 55130ed Compare November 17, 2024 16:33
@Art4
Copy link
Contributor

Art4 commented Jun 26, 2025

I do not think it is really a fixture. I am also considering adding more maintenance scripts (e.g. Rector rules for automatic migration to SimplePie 2).

While reading the rector book I noticed that rector recommends utils/rector/ for custom rules. So I recommend moving the PHPStan files into utils/phpstan/.

File Structure

This is how the file structure for custom rule in your own project will look like:


/src
    SomeCode.php

/utils
    /rector
        /src
            /Rector
                MyFirstRector.php

        /tests
            /Rector
                /MyFirstRector
                    /Fixture
                        test_fixture.php.inc
                    /config
                        config.php
                    MyFirstRectorTest.php

rector.php
composer.json

Copy link
Contributor

@Art4 Art4 left a comment

Choose a reason for hiding this comment

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

LGTM, but we should move the src/PHPStan files into utils/phpstan/. This way we won't need to maintain the build/compile.php and are open for future rector rules.

@jtojnar
Copy link
Member Author

jtojnar commented Jun 27, 2025

Is there any suggestion for namespaces? In the past, I have used Maintenance\Graby\Rector to distinguish it from the top-level Graby namespace. (AIUI, it is better to have one directory per PSR-4 namespace so that the autoloader does not need to check multiple places when looking for a class.)

@Art4
Copy link
Contributor

Art4 commented Jun 27, 2025

The suggested namespaces are:

{
    "autoload": {
        "psr-4": {
            "App\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Utils\\Rector\\": "utils/rector/src",
            "Utils\\Rector\\Tests\\": "utils/rector/tests"
        }
    }
}

I've looked into SimplePie folders and namespaces (ignoring the library folder):

Folder Namespace
src/ SimplePie\
tests/Integration/ SimplePie\Tests\Integration\
tests/Fixtures/ SimplePie\Tests\Fixtures\
tests/Unit/ SimplePie\Tests\Unit\

Following this pattern the utils would have this namespace:

Folder Namespace
utils/ SimplePie\Utils\
utils/PHPStan/ SimplePie\Utils\PHPStan\
utils/PHPStan/Extension/ SimplePie\Utils\PHPStan\Extension\

But I don't like that we restrict us from using the potential SimplePie\Utils\ in the src folder.

What do you think about moving the tests into a separate SimplePieTests namespace and the utils would go into the SimplePieUtils namespace?

Folder Namespace
src/ SimplePie\
tests/ SimplePieTests\
tests/Integration/ SimplePieTests\Integration\
tests/Fixtures/ SimplePieTests\Fixtures\
tests/Unit/ SimplePieTests\Unit\
utils/ SimplePieUtils\
PHPStan extension
utils/PHPStan SimplePieUtils\PHPStan\
utils/PHPStan/Extension SimplePieUtils\PHPStan\Extension\
Potential future Rector rules
utils/Rector SimplePieUtils\Rector\
utils/Rector/Rules SimplePieUtils\Rector\Rules\
utils/Rector/Tests SimplePieUtils\Rector\Tests\

The PSR-4 rules could be condensed into:

{
    "autoload": {
        "psr-4": {
            "SimplePie\\": "src"
        },
        "psr-0": {
            "SimplePie": "library"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "SimplePieTests\\": "tests",
            "SimplePieUtils\\": "utils"
        }
    },
}

@jtojnar
Copy link
Member Author

jtojnar commented Jun 27, 2025

Since tests are internal only, the SimplePie prefix is probably redundant. We could remove it.

I guess they apply the same logic to the internal Rector rules, putting them into Utils.

Looking at some third-party rule sets, RectorSimplePie would be in line:

https://github.com/efabrica-team/rector-nette/blob/40bb22059b380beadae7137b0c20976832916eb2/composer.json#L36
https://github.com/driftingly/rector-laravel/blob/8eaa90a1c62a5a133228dd12d53935eadd0300b8/composer.json#L26

While first-party ones use Rector\\ prefix:

https://github.com/rectorphp/rector-doctrine/blob/0231850ae962c82ff48adc26941475d6fa1b03dc/composer.json#L28
https://github.com/rectorphp/rector-symfony/blob/158a72ea131045f5be7c4ec81beab1938c737277/composer.json#L34

PHPStan appears to follow the same convention for first-party packages:

https://github.com/phpstan/phpstan-phpunit/blob/22c6949f5c0d4d3d343fbd42d2bd75472089d135/composer.json#L35
https://github.com/rectorphp/rector-doctrine/blob/0231850ae962c82ff48adc26941475d6fa1b03dc/composer.json#L28

but the third party ones are bit more wild:

https://github.com/Jan0707/phpstan-prophecy/blob/9c5f67d46d88fb10dd5e674331a35550d51b626c/composer.json#L31
https://github.com/larastan/larastan/blob/b9159cf7f75a668f6341895cbe8d4dcbebd3a775/composer.json#L53
https://github.com/szepeviktor/phpstan-wordpress/blob/4edada80613f627fd320b56b762706a7d4a53b90/composer.json#L34
https://github.com/mglaman/phpstan-drupal/blob/42cca54ee8bccec83a674ac45d1d17586777187e/composer.json#L41

So it really depends on if we want to to expose the extension publicly or not. If yes, SimplePieUtils as you propose sounds best to me.

Though it will probably need to be part of non-dev autoload, unless Composer includes autoload-dev of packages in require-dev. And at that point, we might as well put it in src/. Yes, it will require an extra ignore line in the build.php but I am not hiding my derision for that.

@Art4
Copy link
Contributor

Art4 commented Jun 27, 2025

Good points about autoload-dev and autoload and RectorSimplePie namespace.

Basically, I would say that the src folder is the wrong place for this extension. Consumer of SimplePie should be able to work only with the src-folder to have a minimal set of the required code for using SimplePie (see the workflow of WordPress).

The correct(™️) way to provide an installable PHPStan extension or Rector rules might be separate repositories. But I think this work is not justifiable atm. But having the extension would be very nice.

To have it near our code the SimplePieUtils\PHPStan\ namespace in /utils/PHPStan and SimplePieUtils\Rector\ namespace in /utils/Rector would be a transitional compromise. The utils should be marked as internal. The PHPStanSimplePie or RectorSimplePie namespaces remains free for potential official repositories.

If someone really like to use the PHPStan extension in their project they could copy the extension code or (not recommended) could install the extension manually in their project like this:

composer.json

{
    "require": {
        "simplepie/simplepie": "^1.9"
    },
    "autoload-dev": {
        "psr-4": {
            "SimplePieUtils\\PHPStan\\": "vendor/simplepie/simplepie/utils/PHPStan/"
        }
    }
}

phpstan.neon

includes:
    - vendor/simplepie/simplepie/utils/PHPStan/extension.neon

For potential Rector rules the "installation" could work like this:

composer.json

{
    "require": {
        "simplepie/simplepie": "^1.9"
    },
    "autoload-dev": {
        "psr-4": {
            "SimplePieUtils\\Rector\\": "vendor/simplepie/simplepie/utils/Rector/"
        }
    }
}

rector.php

<?php

use Rector\Config\RectorConfig;
use SimplePieUtils\Rector\Rector\DoingSomeStuffRector;

return RectorConfig::configure()
    ->withRules([
        DoingSomeStuffRector::class
    ]);

jtojnar added 2 commits June 27, 2025 17:08
This will be needed for PHPStan to pass with the next commit.
`Registry::call()` is widely used in SimplePie to allow extensibility. Unfortunately, the pattern hides the actual method from PHPStan so it cannot determine the return value type, and there is no annotation we can add to make it work. , This results in an inadequate analysis.

Thankfully, most cases allow us to determine the method statically so we can create a PHPStan extension doing just that.

We use `SimplePieUtils` instead of `Utils` namespace since this extension might be useful for some SimplePie consumers and do not want to subject them to symbol conflicts. However, we do not currently want to commit to making this a proper Composer package so use it on your own risk, stability not guaranteed.

We may want to add more type checking extensions in the future.
@jtojnar jtojnar force-pushed the wip/jtojnar/phpstan-registry-call branch from 55130ed to 32e7828 Compare June 27, 2025 15:24
@jtojnar jtojnar requested a review from Art4 June 27, 2025 15:25
@jtojnar
Copy link
Member Author

jtojnar commented Jun 27, 2025

  • Removed mention of the extension from README.
  • Removed phpstan installer support.
  • Moved to utils/
  • Switched namespace to SimplePieUtils\PhpStan.
  • Added readme explaining the extension is unstable.

@jtojnar jtojnar merged commit 32e7828 into master Jun 27, 2025
20 checks passed
@jtojnar jtojnar deleted the wip/jtojnar/phpstan-registry-call branch June 27, 2025 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants