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

luttje / filament-user-attributes / 6913993013

18 Nov 2023 12:43PM UTC coverage: 63.312% (+4.0%) from 59.278%
6913993013

push

github

luttje
simplify readme

906 of 1431 relevant lines covered (63.31%)

19.3 hits per line

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

95.28
/src/CodeGeneration/CodeEditor.php
1
<?php
2

3
namespace Luttje\FilamentUserAttributes\CodeGeneration;
4

5
use PhpParser\NodeTraverser;
6
use PhpParser\PrettyPrinter;
7
use PhpParser\NodeVisitor;
8

9
/**
10
 * @internal
11
 */
12
final class CodeEditor
13
{
14
    protected static $recentBackupPaths = [];
15

16
    public static function make()
36✔
17
    {
18
        return new static();
36✔
19
    }
20

21
    public static function clearRecentBackupPaths()
4✔
22
    {
23
        static::$recentBackupPaths = [];
4✔
24
    }
25

26
    public static function getRecentBackupPaths()
4✔
27
    {
28
        return static::$recentBackupPaths;
4✔
29
    }
30

31
    public function editFileWithBackup($path, $callback)
8✔
32
    {
33
        $transaction = new CodeEditTransaction($path);
8✔
34
        $transaction->edit($callback);
8✔
35

36
        $backupPath = $transaction->getBackupFilePath();
8✔
37

38
        if ($backupPath) {
8✔
39
            self::$recentBackupPaths[$path] = $backupPath;
8✔
40
        }
41

42
        return $transaction;
8✔
43
    }
44

45
    public function addTrait($code, $trait)
8✔
46
    {
47
        return self::modifyCode(
8✔
48
            $code,
8✔
49
            self::fullyQualifyClass($trait),
8✔
50
            TraitInserter::class
8✔
51
        );
8✔
52
    }
53

54
    public function addInterface($code, $interface)
8✔
55
    {
56
        return self::modifyCode(
8✔
57
            $code,
8✔
58
            self::fullyQualifyClass($interface),
8✔
59
            InterfaceInserter::class
8✔
60
        );
8✔
61
    }
62

63
    public function addMethod($code, $methodName, ?\Closure $builder = null)
4✔
64
    {
65
        return self::modifyCode(
4✔
66
            $code,
4✔
67
            $methodName,
4✔
68
            MethodInserter::class,
4✔
69
            $builder
4✔
70
        );
4✔
71
    }
72

73
    public function modifyMethod($code, $methodName, ?\Closure $builder = null)
16✔
74
    {
75
        return self::modifyCode(
16✔
76
            $code,
16✔
77
            $methodName,
16✔
78
            MethodModifier::class,
16✔
79
            $builder
16✔
80
        );
16✔
81
    }
82

83
    private static function traverseUntil($ast, $callback)
12✔
84
    {
85
        $traverser = new NodeTraverser();
12✔
86
        $visitor = new NodeVisitor\FirstFindingVisitor($callback);
12✔
87
        $traverser->addVisitor($visitor);
12✔
88
        $traverser->traverse($ast);
12✔
89

90
        return $visitor->getFoundNode();
12✔
91
    }
92

93
    private static function traverseUntilUsing($ast, $symbolFilter)
20✔
94
    {
95
        $traverser = new NodeTraverser();
20✔
96
        $visitor = new UsingCollector($symbolFilter);
20✔
97
        $traverser->addVisitor($visitor);
20✔
98
        $traverser->traverse($ast);
20✔
99

100
        return $visitor->getFoundNode();
20✔
101
    }
102

103
    private static function parseAndTraverseUntilUsing($code, $symbolFilter)
20✔
104
    {
105
        [$parser, $lexer] = self::makeParserWithLexer();
20✔
106
        $ast = $parser->parse($code);
20✔
107

108
        return self::traverseUntilUsing($ast, $symbolFilter);
20✔
109
    }
110

111
    public static function usesTrait($code, $trait)
20✔
112
    {
113
        $trait = self::fullyQualifyClass($trait);
20✔
114
        $node = self::parseAndTraverseUntilUsing($code, $trait);
20✔
115

116
        return $node !== null;
20✔
117
    }
118

119
    public static function implementsInterface($code, $interface)
20✔
120
    {
121
        $interface = self::fullyQualifyClass($interface);
20✔
122
        $node = self::parseAndTraverseUntilUsing($code, $interface);
20✔
123

124
        return $node !== null;
20✔
125
    }
126

127
    public static function fullyQualifyClass($class)
40✔
128
    {
129
        if (strpos($class, '\\') === 0) {
40✔
130
            return $class;
×
131
        }
132

133
        return '\\' . $class;
40✔
134
    }
135

136
    private static function makeParserWithLexer()
56✔
137
    {
138
        $lexer = new \PhpParser\Lexer\Emulative([
56✔
139
            'usedAttributes' => [
56✔
140
                'comments',
56✔
141
                'startLine', 'endLine',
56✔
142
                'startTokenPos', 'endTokenPos',
56✔
143
            ],
56✔
144
        ]);
56✔
145
        $parser = new \PhpParser\Parser\Php7($lexer);
56✔
146

147
        return [$parser, $lexer];
56✔
148
    }
149

150
    public static function astFromTemplate($template)
×
151
    {
152
        [$parser, $lexer] = self::makeParserWithLexer();
×
153
        return $parser->parse($template);
×
154
    }
155

156
    private static function modifyCode($code, $nodeName, $modifierClass, ?\Closure $builder = null)
36✔
157
    {
158
        [$parser, $lexer] = self::makeParserWithLexer();
36✔
159

160
        // Create a traverser to clone the AST so we can keep the original formatting
161
        $traverser = new NodeTraverser();
36✔
162
        $traverser->addVisitor(new NodeVisitor\CloningVisitor());
36✔
163

164
        $ast = $parser->parse($code);
36✔
165
        $origTokens = $lexer->getTokens();
36✔
166

167
        $modifiedAst = $traverser->traverse($ast);
36✔
168

169
        // Create a new traverser to modify the AST
170
        $traverser = new NodeTraverser();
36✔
171
        $traverser->addVisitor(new $modifierClass($nodeName, $builder));
36✔
172

173
        $modifiedAst = $traverser->traverse($modifiedAst);
36✔
174

175
        // Preserve most formatting
176
        $prettyPrinter = new PrettyPrinter\Standard();
36✔
177
        return $prettyPrinter->printFormatPreserving($modifiedAst, $ast, $origTokens);
36✔
178
    }
179

180
    /**
181
     * Recrusively traverse the statements and find the variable on this method is called.
182
     *
183
     * @param \PhpParser\Node $node A method or variable
184
     */
185
    private static function getOriginalVariableName($node)
12✔
186
    {
187
        if ($node instanceof \PhpParser\Node\Expr\MethodCall) {
12✔
188
            return self::getOriginalVariableName($node->var);
12✔
189
        } elseif ($node instanceof \PhpParser\Node\Expr\Variable) {
12✔
190
            return $node->name;
12✔
191
        }
192

193
        return false;
×
194
    }
195

196
    /**
197
     * Traverse the statements and find the variable on which the given method is called.
198
     */
199
    public static function findCall($stmts, $variableName, $methodName)
12✔
200
    {
201
        return self::traverseUntil($stmts, function ($node) use ($variableName, $methodName) {
12✔
202
            if (!($node instanceof \PhpParser\Node\Expr\MethodCall)) {
12✔
203
                return false;
12✔
204
            }
205

206
            $varName = self::getOriginalVariableName($node->var);
12✔
207

208
            return $varName === $variableName
12✔
209
                && $node->name->name === $methodName;
12✔
210
        });
12✔
211
    }
212
}
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