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

adriansuter / php-autoload-override / 12951325128

24 Jan 2025 02:10PM UTC coverage: 93.235% (-5.3%) from 98.529%
12951325128

push

github

adriansuter
Update phpunit.

317 of 340 relevant lines covered (93.24%)

3.95 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 is_null;
27
use function md5;
28
use function str_replace;
29
use function uniqid;
30

31
/**
32
 * @package AdrianSuter\Autoload\Override
33
 */
34
class CodeConverter
35
{
36
    private const ATTR_RESOLVED_NAME = 'resolvedName';
37

38
    protected Parser $parser;
39

40
    protected NodeTraverser $traverser;
41

42
    protected Standard $printer;
43

44
    protected NodeFinder $nodeFinder;
45

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

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

68
        $this->printer = $printer ?? new Standard();
2✔
69

70
        $this->nodeFinder = $nodeFinder ?? new NodeFinder();
2✔
71
    }
72

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

88
        $oldTokens = $this->parser->getTokens();
1✔
89

90
        $newStmts = $this->traverser->traverse($oldStmts);
1✔
91

92
        // Find function calls.
93
        $overridePlaceholders = [];
1✔
94
        $funcCalls = $this->nodeFinder->findInstanceOf($newStmts, FuncCall::class);
1✔
95
        foreach ($funcCalls as $funcCall) {
1✔
96
            /** @var FuncCall $funcCall */
97
            if (!$funcCall->name->hasAttribute(self::ATTR_RESOLVED_NAME)) {
1✔
98
                // This function call has no resolved fully qualified name.
99
                continue;
×
100
            }
101

102
            /** @var FullyQualified $resolvedName */
103
            $resolvedName = $funcCall->name->getAttribute(self::ATTR_RESOLVED_NAME);
1✔
104

105
            $resolvedNameCode = $resolvedName->toCodeString();
1✔
106
            if (isset($functionCallMap[$resolvedNameCode])) {
1✔
107
                // There is a function call map > Create a unique key.
108
                $key = uniqid(md5($resolvedNameCode), true);
1✔
109

110
                // Put the key into the overridePlaceholders array as at the end we need to
111
                // replace those keys with the corresponding target function call.
112
                $overridePlaceholders[$key] = $functionCallMap[$resolvedNameCode];
1✔
113

114
                // Replace the name to be the fully qualified name, i.e. the given unique key
115
                // (we will replace that at the end).
116
                $funcCall->name = new FullyQualified($key);
1✔
117
            }
118
        }
119

120
        // Print the source code.
121
        $code = $this->printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
1✔
122

123
        // Return the source code if there are no override placeholders.
124
        if (empty($overridePlaceholders)) {
1✔
125
            return $code;
×
126
        }
127

128
        // Replace all override placeholders by their target function call.
129
        return str_replace(array_keys($overridePlaceholders), array_values($overridePlaceholders), $code);
1✔
130
    }
131
}
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