Page MenuHomePhabricator

Phan taint check plugin crashes in composer/pcre: Using ${var} in strings is deprecated, use {$var} instead
Closed, DeclinedPublic

Description

Running Phan with mediawiki/extensions/timeline gives me:

./projects/mediawiki/extensions/timeline/vendor/mediawiki/phan-taint-check-plugin/src/SecurityCheckPlugin.php:606 [8192] Using ${var} in strings is deprecated, use {$var} instead
(Phan 5.4.1 crashed when parsing/analyzing './projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php')
stacktrace
More details:
#2: include() called at [./projects/mediawiki/extensions/timeline/vendor/composer/ClassLoader.php:576] Args: ["./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Bootstrap.php"]
#3: Composer\Autoload\{closure}() called at [./projects/mediawiki/extensions/timeline/vendor/composer/ClassLoader.php:427] Args: ["./projects/mediawiki/extensions/timeline/vendor/composer/../mediawiki/phan-taint-check-plugin/src/SecurityCheckPlugin.php"]
#4: Composer\Autoload\ClassLoader->loadClass() called at [./projects/mediawiki/extensions/timeline/vendor/mediawiki/phan-taint-check-plugin/MediaWikiSecurityCheckPlugin.php:39] Args: ["SecurityCheckPlugin\\SecurityCheckPlugin"]
#5: require() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Plugin/ConfigPluginSet.php:939] Args: ["./projects/mediawiki/extensions/timeline/vendor/mediawiki/phan-taint-check-plugin/MediaWikiSecurityCheckPlugin.php"]
#6: Phan\Plugin\ConfigPluginSet::Phan\Plugin\{closure}() Args: ["./projects/mediawiki/extensions/timeline/vendor/mediawiki/mediawiki-phan-config/src/../../phan-taint-check-plugin/MediaWikiSecurityCheckPlugin.php"]
#7: array_map() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Plugin/ConfigPluginSet.php:971] Args: [Closure, ["PregRegexCheckerPlugin", "UnusedSuppressionPlugin", "DuplicateExpressionPlugin", "LoopVariableReusePlugin", "RedundantAssignmentPlugin", "UnreachableCodePlugin", "SimplifyExpressionPlugin", "DuplicateArrayKeyPlugin", "UseReturnValuePlugin", "AddNeverReturnTypePlugin", ... 1 more element(s)]]
#8: Phan\Plugin\ConfigPluginSet->ensurePluginsExist() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Plugin/ConfigPluginSet.php:237]
#9: Phan\Plugin\ConfigPluginSet::newInstance() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Plugin/ConfigPluginSet.php:223]
#10: Phan\Plugin\ConfigPluginSet::instance() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Issue.php:6198]
#11: Phan\Issue::shouldSuppressIssue() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Issue.php:6146] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:28), "PhanUnextractableAnnotationSuffix", 26, ["* @var 0|positive-int", "0|positive", "-"], null]
#12: Phan\Issue::maybeEmitWithParameters() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Language/Element/Comment/Builder.php:1588] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:28), "PhanUnextractableAnnotationSuffix", 26, ["* @var 0|positive-int", "0|positive", "-"], null]
#13: Phan\Language\Element\Comment\Builder->emitDeferredIssues() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Language/Element/Comment/Builder.php:346]
#14: Phan\Language\Element\Comment\Builder->build() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Language/Element/Comment.php:423]
#15: Phan\Language\Element\Comment::fromStringInContext() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Parse/ParseVisitor.php:568] Args: ["/**\n     * @readonly\n     * @var 0|positive-int\n     */", Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:28), 28, 3]
#16: Phan\Parse\ParseVisitor->visitPropGroup() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Analysis.php:190] Args: [ast\Node({"kind":774,"flags":1,"lineno":28,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":28,"children":[{"kind":775,"flags":0,"lineno":28,"children":{"name":"count","default":null,"docComment":"/**\n     * @readonly\n     * @var 0|positive-int\n     */"}}]},"attributes":null}})]
#17: Phan\Analysis::parseNodeInContext() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Analysis.php:217] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:28), ast\Node({"kind":774,"flags":1,"lineno":28,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":28,"children":[{"kind":775,"flags":0,"lineno":28,"children":{"name":"count","default":null,"docComment":"/**\n     * @readonly\n     * @var 0|positive-int\n     */"}}]},"attributes":null}})]
#18: Phan\Analysis::parseNodeInContext() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Analysis.php:217] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:28), ast\Node({"kind":132,"flags":0,"lineno":15,"children":[{"kind":774,"flags":1,"lineno":22,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":22,"children":[{"kind":775,"flags":0,"lineno":22,"children":{"name":"matches","default":null,"docComment":"/**\n     * An array of match group => list of matched strings\n     *\n     * @readonly\n     * @var array<int|string, list<string|null>>\n     */"}}]},"attributes":null}},{"kind":774,"flags":1,"lineno":28,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":28,"children":[{"kind":775,"flags":0,"lineno":28,"children":{"name":"count","default":null,"docComment":"/**\n     * @readonly\n     * @var 0|positive-int\n     */"}}]},"attributes":null}},{"kin...
#19: Phan\Analysis::parseNodeInContext() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Analysis.php:217] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:14), ast\Node({"kind":70,"flags":32,"lineno":14,"children":{"name":"MatchAllResult","docComment":null,"extends":null,"implements":null,"stmts":{"kind":132,"flags":0,"lineno":15,"children":[{"kind":774,"flags":1,"lineno":22,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":22,"children":[{"kind":775,"flags":0,"lineno":22,"children":{"name":"matches","default":null,"docComment":"/**\n     * An array of match group => list of matched strings\n     *\n     * @readonly\n     * @var array<int|string, list<string|null>>\n     */"}}]},"attributes":null}},{"kind":774,"flags":1,"lineno":28,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":28,"children":[{"kind":775,"flags":0,"lineno":28,"children":{"nam...
#20: Phan\Analysis::parseNodeInContext() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Analysis.php:142] Args: [Phan\CodeBase({}), Phan\Language\Context(./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php:12), ast\Node({"kind":132,"flags":0,"lineno":1,"children":[{"kind":542,"flags":0,"lineno":12,"children":{"name":"Composer\\Pcre","stmts":null}},{"kind":70,"flags":32,"lineno":14,"children":{"name":"MatchAllResult","docComment":null,"extends":null,"implements":null,"stmts":{"kind":132,"flags":0,"lineno":15,"children":[{"kind":774,"flags":1,"lineno":22,"children":{"type":null,"props":{"kind":138,"flags":0,"lineno":22,"children":[{"kind":775,"flags":0,"lineno":22,"children":{"name":"matches","default":null,"docComment":"/**\n     * An array of match group => list of matched strings\n     *\n     * @readonly\n     * @var array<int|string, list<string|null>>\n     */"}}]},"attributes":null}},{"kind":774,"flags":1,"lineno":2...
#21: Phan\Analysis::parseFile() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/Phan/Phan.php:295] Args: [Phan\CodeBase({}), "./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php"]
#22: Phan\Phan::analyzeFileList() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/phan.php:30] Args: [Phan\CodeBase({}), Closure]
#23: require_once() called at [./projects/mediawiki/extensions/timeline/vendor/phan/phan/phan:10] Args: ["./projects/mediawiki/extensions/timeline/vendor/phan/phan/src/phan.php"]
#24: include() called at [./projects/mediawiki/extensions/timeline/vendor/bin/phan:119] Args: ["./projects/mediawiki/extensions/timeline/vendor/phan/phan/phan"]
(Some long strings (usually JSON of AST Nodes) were truncated. To print more details for some stack frames of this crash, increase the Phan config setting debug_max_frame_length)

My environment:

php8.2.7
php-ast1.1.0
composer2.6.4

Direct dependencies:

mediawiki/mediawiki-codesnifferv41.0.0
mediawiki/mediawiki-phan-config0.12.1
mediawiki/minus-x1.1.1
php-parallel-lint/php-console-highlighterv1.0.0
php-parallel-lint/php-parallel-lintv1.3.2

Looks like the plugin choke on composer/pcre which comes as a transitive dependency of phan:

phan/phancomposer/xdebug-handlercomposer/pcre

It took me a while to find out the issue refers to the composer dependency from mediawiki/core rather than the one from the extension (path is ./projects/mediawiki/core/vendor/composer/pcre/src/MatchAllResult.php).

Event Timeline

Translate had a similar one at T322439 and was fixed by https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Translate/+/852934/1/TranslateHooks.php

It looks like the issue is specific to PHP 8.2 support and the canonical task T314096. The issue I have though is that no PHP code in the timeline extension uses the ${var} pattern for interpolation.


If I upgrade phan config, it still fails albeit at a different point:

$ composer require --dev mediawiki/mediawiki-phan-config 0.13.0
...
  - Upgrading microsoft/tolerant-php-parser (v0.1.1 => v0.1.2): Extracting archive
  - Upgrading phan/phan (5.4.1 => 5.4.2): Extracting archive
  - Upgrading mediawiki/phan-taint-check-plugin (4.0.0 => 5.0.0): Extracting archive
  - Upgrading mediawiki/mediawiki-phan-config (0.12.1 => 0.13.0): Extracting archive
$ vendor/bin/phan

./mediawiki/extensions/timeline/vendor/mediawiki/phan-taint-check-plugin/src/TaintednessAccessorsTrait.php:39 [8192] Creation of dynamic property Phan\Language\Element\Property::$taintedness is deprecated
(Phan 5.4.2 crashed when parsing/analyzing 'includes/Timeline.php')
More details:
#2: SecurityCheckPlugin\PreTaintednessVisitor::setTaintednessRaw() called at [./projects/mediawiki/extensions/timeline/vendor/mediawiki/phan-taint-check-plugin/src/TaintednessBaseVisitor.php:256] Args: [Phan\Language\Element\Property(private static \FileBackend|null $backend), SecurityCheckPlugin\Taintedness({})]
...

Which looks like T325321: phan-taint-check-plugin depends on creation of dynamic properties which gets deprecated in php8.2

mediawiki/mediawiki-phan-config 0.12.1

This issue has already been fixed in the latest version of taint-check (5.0.0, available in mediawiki-phan-config 0.13.0) in r862314 (which looks like it doesn't have an associated phab task).

But, even if you upgrade phan, it won't work on PHP 8.2 due to T325321, which is pretty much blocked on phan having become semi-abandoned :(

Thanks! So the summary (for my later self) is:

  • Using ${var} in strings is deprecated, use {$var} instead is fixed by taint-check 5.0.0
  • Phan doesn't work with 8.2
  • Conclusion: use php 8.1 or earlier

I am declining this task since the php 8.2 issue is already tracked (T35321) and a fix at https://gerrit.wikimedia.org/r/c/mediawiki/tools/phan/SecurityCheckPlugin/+/862314 :)

Thank you @Daimona !