Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ public function clean(string $contents, int $maxMatches): string
if (isset($this->typeConfig[$char])) {
$type = $this->typeConfig[$char];

if (substr($this->contents, $this->index, $type['length']) === $type['name']) {
if (
substr($this->contents, $this->index, $type['length']) === $type['name']
&& $this->match('~.\\b(?<![\\$:>])' . $type['name'] . '\\b~Ais', $match, $this->index - 1)
) {
if ($maxMatches === 1 && $this->match($type['pattern'], $match, $this->index - 1)) {
return $clean . $match[0];
}
Expand Down
2 changes: 1 addition & 1 deletion src/Turbo/TurboExtensionEnabler.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
final class TurboExtensionEnabler
{

public const EXPECTED_EXTENSION_VERSION = '1cb5faf';
public const EXPECTED_EXTENSION_VERSION = '1309323';

private static bool $active = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ final class SymbolFinderInFilesTest extends PHPStanTestCase
*/
public static function dataFindSymbols(): iterable
{
yield 'class name expressions do not hide later global constants' => [
__DIR__ . '/data/symbol-finder/class-name-before-global-constant.php',
[
[],
['symbolfinderclassname', 'classname', 'symbolfinderanonymousclass'],
['AFTER_CLASS_NAME', 'AFTER_CLASS_PREFIX', 'AFTER_ANONYMOUS_CLASS'],
],
];

yield 'namespaced constants do not leak a class entry' => [
__DIR__ . '/data/symbol-finder/namespaced-constants.php',
[
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php declare(strict_types = 1);

function symbolFinderClassName(): string
{
return \stdClass::class;
}

const AFTER_CLASS_NAME = 'found';

function className(): string
{
return 'not a class declaration';
}

const AFTER_CLASS_PREFIX = 'still found';

function symbolFinderAnonymousClass(): object
{
return new class {
public const MEMBER = 'not global';
};
}

const AFTER_ANONYMOUS_CLASS = 'also found';
8 changes: 6 additions & 2 deletions turbo-ext/src/SymbolScan.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,12 @@ inline void PhpFileCleaner::clean(zend_long maxMatches, std::string &out)
for (const TypeConfig &type : types) {
if (type.firstByte != c) continue;

if (index + type.length <= len && memcmp(contents + index, type.name, type.length) == 0) {
if (maxMatches == 1 && prevByteOpensKeyword(index)) {
if (index + type.length <= len
&& memcmp(contents + index, type.name, type.length) == 0
&& prevByteOpensKeyword(index)
&& (index + type.length == len || !isWordByte((unsigned char) contents[index + type.length]))
) {
if (maxMatches == 1) {
size_t end;
if (matchSpacesAndName(index + type.length, &end)) {
clean.append(contents + index - 1, end - (index - 1));
Expand Down
1 change: 1 addition & 0 deletions turbo-ext/tests/php-file-cleaner-corpus.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
'short echo tag' => "<?php\nclass A {}\n?>\n<?= 'class NotAClass' ?>\n<?php class B {}\n",
'anon class' => "<?php\n\$a = new class extends Foo implements Bar {};\nclass Real {}\n",
'property named class' => "<?php\nclass A { public \$class = 1; }\n\$x = Foo::class;\n\$y = \$obj->class;\n",
'class name before global constant' => "<?php\nfunction f() { return Foo::class; }\nconst AFTER_CLASS_NAME = 1;\nfunction className() {}\nconst AFTER_CLASS_PREFIX = 2;\n",
'function by reference' => "<?php\nfunction &refFn() {}\nfunction plain(): void {}\n",
'namespace braces' => "<?php\nnamespace A\\B { class C {} }\nnamespace D { const E = 1; }\n",
'no php tag' => "just text, no php at all\n",
Expand Down
1 change: 1 addition & 0 deletions turbo-ext/tests/symbol-finder-corpus.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
'interface const' => "<?php\ninterface I { const X = 1; }\n",
'enum const' => "<?php\nenum E: string { case A = 'a'; const X = 1; }\n",
'global constants' => "<?php\nconst A = 1;\nconst B = 2;\n",
'class name before global constant' => "<?php\nfunction f() { return Foo::class; }\nconst AFTER_CLASS_NAME = 1;\nfunction className() {}\nconst AFTER_CLASS_PREFIX = 2;\n",
'define' => "<?php\ndefine('FOO', 1);\ndefine(\"N\\\\BAR\", 2);\ndefine('N\\\\\\\\BAZ', 3);\n",
'define leading slash' => "<?php\n\\define('SLASHED', 1);\n",
'define with variable' => "<?php\ndefine(\$name, 1);\n\$x = 'function evil(';\n",
Expand Down
Loading