• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adriansuter / php-autoload-override / 24449574994

15 Apr 2026 10:29AM UTC coverage: 88.952%. Remained the same
24449574994

push

github

adriansuter
Micro optimizations.

7 of 7 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

314 of 353 relevant lines covered (88.95%)

4.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/src/CodeConverter.php
1
<?php
2

3
/**
4
 * PHP Autoload Override (https://github.com/adriansuter/php-autoload-override)
5
 *
6
 * @license https://github.com/adriansuter/php-autoload-override/blob/master/LICENSE.md (MIT License)
7
 */
8

9
declare(strict_types=1);
10

11
namespace AdrianSuter\Autoload\Override;
12

13
use PhpParser\Node\Expr\FuncCall;
14
use PhpParser\Node\Name\FullyQualified;
15
use PhpParser\NodeFinder;
16
use PhpParser\NodeTraverser;
17
use PhpParser\NodeVisitor\CloningVisitor;
18
use PhpParser\NodeVisitor\NameResolver;
19
use PhpParser\Parser;
20
use PhpParser\ParserFactory;
21
use PhpParser\PrettyPrinter\Standard;
22
use RuntimeException;
23

24
use function array_keys;
25
use function array_values;
26
use function str_replace;
27

28
/**
29
 * @package AdrianSuter\Autoload\Override
30
 */
31
class CodeConverter
32
{
33
    private const ATTR_RESOLVED_NAME = 'resolvedName';
34

35
    protected Parser $parser;
36

37
    protected NodeTraverser $traverser;
38

39
    protected Standard $printer;
40

41
    protected NodeFinder $nodeFinder;
42

43
    /**
44
     * @param Parser|null $parser The PHP Parser.
45
     * @param NodeTraverser|null $traverser The PHP Node Traverser - make sure that the traverser has a CloningVisitor
46
     *                                      and a NameResolver visitor.
47
     * @param Standard|null $printer The PHP Printer.
48
     * @param NodeFinder|null $nodeFinder The PHP Node Finder.
49
     */
50
    public function __construct(
51
        ?Parser $parser = null,
52
        ?NodeTraverser $traverser = null,
53
        ?Standard $printer = null,
54
        ?NodeFinder $nodeFinder = null
55
    ) {
56
        $this->parser = $parser ?? (new ParserFactory())->createForNewestSupportedVersion();
2✔
57

58
        if ($traverser === null) {
2✔
59
            $traverser = new NodeTraverser();
2✔
60
            $traverser->addVisitor(new CloningVisitor());
2✔
61
            $traverser->addVisitor(new NameResolver(null, ['replaceNodes' => false]));
2✔
62
        }
63
        $this->traverser = $traverser;
2✔
64

65
        $this->printer = $printer ?? new Standard();
2✔
66

67
        $this->nodeFinder = $nodeFinder ?? new NodeFinder();
2✔
68
    }
69

70
    /**
71
     * Convert the given source code.
72
     *
73
     * @param string $code The source code.
74
     * @param array<string, string> $functionCallMap The function call map.
75
     *
76
     * @return string
77
     */
78
    public function convert(string $code, array $functionCallMap): string
79
    {
80
        $oldStmts = $this->parser->parse($code);
2✔
81
        if ($oldStmts === null) {
2✔
82
            throw new RuntimeException('Code could not be parsed.');
1✔
83
        }
84

85
        $oldTokens = $this->parser->getTokens();
1✔
86

87
        $newStmts = $this->traverser->traverse($oldStmts);
1✔
88

89
        // Find function calls.
90
        $overridePlaceholders = [];
1✔
91
        $funcCalls = $this->nodeFinder->findInstanceOf($newStmts, FuncCall::class);
1✔
92
        foreach ($funcCalls as $funcCall) {
1✔
93
            /** @var FuncCall $funcCall */
94
            $resolvedName = $funcCall->name->getAttribute(self::ATTR_RESOLVED_NAME);
1✔
95
            if (!$resolvedName instanceof FullyQualified) {
1✔
96
                continue;
×
97
            }
98

99
            $resolvedNameCode = $resolvedName->toCodeString();
1✔
100
            if (isset($functionCallMap[$resolvedNameCode])) {
1✔
101
                // There is a function call map > Create a unique key.
102
                $key = '__override_' . spl_object_id($funcCall);
1✔
103

104
                // Put the key into the overridePlaceholders array as at the end we need to
105
                // replace those keys with the corresponding target function call.
106
                $overridePlaceholders[$key] = $functionCallMap[$resolvedNameCode];
1✔
107

108
                // Replace the name to be the fully qualified name, i.e. the given unique key
109
                // (we will replace that at the end).
110
                $funcCall->name = new FullyQualified($key);
1✔
111
            }
112
        }
113

114
        // Print the source code.
115
        $code = $this->printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
1✔
116

117
        // Return the source code if there are no override placeholders.
118
        if ($overridePlaceholders === []) {
1✔
UNCOV
119
            return $code;
×
120
        }
121

122
        // Replace all override placeholders by their target function call.
123
        return str_replace(array_keys($overridePlaceholders), array_values($overridePlaceholders), $code);
1✔
124
    }
125
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc