Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
59 / 59 |
|
100.00% |
9 / 9 |
CRAP | |
100.00% |
1 / 1 |
| BasicGenerationHelper | |
100.00% |
59 / 59 |
|
100.00% |
9 / 9 |
21 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| return | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| addVisibility | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
| createDocComment | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
6 | |||
| createArguments | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
| createArray | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| convertToAstNode | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
| isValidDefault | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| generateFile | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace quintenmbusiness\PhpAstCodeGenerationHelper\GeneratorHelpers; |
| 6 | |
| 7 | use PhpParser\Builder\Namespace_; |
| 8 | use PhpParser\Node\Arg; |
| 9 | use PhpParser\Node\Expr; |
| 10 | use PhpParser\Builder\Method; |
| 11 | use PhpParser\BuilderFactory; |
| 12 | use PhpParser\Builder\Property; |
| 13 | use PhpParser\Node\Expr\Array_; |
| 14 | use PhpParser\Node\Expr\ArrayItem; |
| 15 | use PhpParser\Node\Expr\Cast\Bool_; |
| 16 | use PhpParser\Node\Expr\ConstFetch; |
| 17 | use PhpParser\Node\Expr\Variable; |
| 18 | use PhpParser\Node\Name; |
| 19 | use PhpParser\Node\Scalar\String_; |
| 20 | use PhpParser\Node\Scalar\LNumber; |
| 21 | use PhpParser\Node\Stmt\Return_; |
| 22 | use PhpParser\Builder\Class_; |
| 23 | use PhpParser\Builder\Interface_; |
| 24 | use PhpParser\PrettyPrinter\Standard; |
| 25 | |
| 26 | class BasicGenerationHelper |
| 27 | { |
| 28 | /** |
| 29 | * @var BuilderFactory |
| 30 | */ |
| 31 | public BuilderFactory $factory; |
| 32 | |
| 33 | /** |
| 34 | * @param BuilderFactory $factory |
| 35 | */ |
| 36 | public function __construct(BuilderFactory $factory) |
| 37 | { |
| 38 | $this->factory = $factory; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Creates a return statement using a passed Expr |
| 43 | * |
| 44 | * @param Expr $expr |
| 45 | * @return Return_ |
| 46 | */ |
| 47 | public function return(Expr $expr): Return_ |
| 48 | { |
| 49 | return new Return_($expr); |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * @param Method|Property $stmt |
| 54 | * @param string $visibility |
| 55 | * @return Method|Property |
| 56 | */ |
| 57 | public function addVisibility(Method|Property $stmt, string $visibility = 'public'): Method|Property |
| 58 | { |
| 59 | match ($visibility) { |
| 60 | 'public' => $stmt->makePublic(), |
| 61 | 'protected' => $stmt->makeProtected(), |
| 62 | 'private' => $stmt->makePrivate(), |
| 63 | 'static' => $stmt->makeStatic(), |
| 64 | 'abstract' => $stmt->makeAbstract(), |
| 65 | 'final' => $stmt->makeFinal(), |
| 66 | default => $stmt, |
| 67 | }; |
| 68 | |
| 69 | return $stmt; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Adds a PHPDoc comment to the given Method or Property. |
| 74 | * |
| 75 | * @param Method|Property $method |
| 76 | * @param array<int, array<string, string>> $params Array of parameters, where each element has 'type' and 'name'. |
| 77 | * @param null|string $returnType The return type to document. |
| 78 | * @param null|string $throws The exception type to document in @throws. |
| 79 | * @param null|string $deprecated Marks the method/property as deprecated with an optional message. |
| 80 | * @param array<string, string> $additional Additional tags, where the key is the tag (e.g., 'author') and the value is the tag's content. |
| 81 | * @return Method|Property |
| 82 | */ |
| 83 | public function createDocComment( |
| 84 | Method|Property $method, |
| 85 | array $params = [], |
| 86 | ?string $returnType = null, |
| 87 | ?string $throws = null, |
| 88 | ?string $deprecated = null, |
| 89 | array $additional = [] |
| 90 | ): Method|Property { |
| 91 | $comment = "\n" . '/**'; |
| 92 | |
| 93 | // Add @param tags |
| 94 | foreach ($params as $param) { |
| 95 | $comment .= "\n" . ' * @param ' . $param['type'] . ' $' . $param['name']; |
| 96 | } |
| 97 | |
| 98 | // Add @return tag |
| 99 | if (!empty($returnType)) { |
| 100 | $comment .= "\n * \n" . ' * @return ' . $returnType; |
| 101 | } |
| 102 | |
| 103 | // Add @throws tag |
| 104 | if (!empty($throws)) { |
| 105 | $comment .= "\n" . ' * @throws ' . $throws; |
| 106 | } |
| 107 | |
| 108 | // Add @deprecated tag |
| 109 | if (!empty($deprecated)) { |
| 110 | $comment .= "\n" . ' * @deprecated ' . $deprecated; |
| 111 | } |
| 112 | |
| 113 | // Add any additional tags |
| 114 | foreach ($additional as $tag => $content) { |
| 115 | $comment .= "\n" . ' * @' . $tag . ' ' . $content; |
| 116 | } |
| 117 | |
| 118 | $comment .= "\n" . ' */'; |
| 119 | |
| 120 | $method->setDocComment($comment); |
| 121 | |
| 122 | return $method; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Creates an array of arguments for a method call. |
| 127 | * |
| 128 | * @param array<int|string, mixed> $args |
| 129 | * @return array<int, Arg> |
| 130 | */ |
| 131 | public function createArguments(array $args): array |
| 132 | { |
| 133 | $arguments = []; |
| 134 | foreach ($args as $value) { |
| 135 | $arguments[] = new Arg($value instanceof Expr ? $value : $this->convertToAstNode($value)); |
| 136 | } |
| 137 | return $arguments; |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * Creates an array node from a PHP array. |
| 142 | * |
| 143 | * @param array<int|string, mixed> $items |
| 144 | * @return Expr\Array_ |
| 145 | */ |
| 146 | public function createArray(array $items): Array_ |
| 147 | { |
| 148 | $arrayItems = []; |
| 149 | foreach ($items as $key => $value) { |
| 150 | $keyNode = is_int($key) ? null : $this->convertToAstNode($key); |
| 151 | $arrayItems[] = new ArrayItem($this->convertToAstNode($value), $keyNode); |
| 152 | } |
| 153 | |
| 154 | return new Array_($arrayItems); |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * Converts a PHP value to an AST node. |
| 159 | * |
| 160 | * @param mixed $value |
| 161 | * @return Array_|LNumber|String_|ConstFetch|Variable |
| 162 | * @throws \InvalidArgumentException |
| 163 | */ |
| 164 | public function convertToAstNode(mixed $value): Array_|LNumber|String_|ConstFetch|Variable |
| 165 | { |
| 166 | return match (true) { |
| 167 | is_int($value) => new LNumber($value), |
| 168 | is_string($value) => new String_($value), |
| 169 | is_array($value) => $this->createArray($value), |
| 170 | is_bool($value) => new ConstFetch(new Name($value ? 'true' : 'false')), |
| 171 | $value instanceof Variable => $value, |
| 172 | $value === null => new ConstFetch(new Name('null')), // Handle null as ConstFetch |
| 173 | default => throw new \InvalidArgumentException('Unsupported value type for AST conversion: ' . gettype($value)), |
| 174 | }; |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * @param string|null $type |
| 179 | * @param mixed $default |
| 180 | * @return bool |
| 181 | */ |
| 182 | public function isValidDefault(?string $type, mixed $default): bool |
| 183 | { |
| 184 | return match ($type) { |
| 185 | 'int' => is_int($default), |
| 186 | 'string' => is_string($default), |
| 187 | 'bool' => is_bool($default), |
| 188 | 'float' => is_float($default), |
| 189 | 'array' => is_array($default), |
| 190 | default => false, |
| 191 | }; |
| 192 | } |
| 193 | |
| 194 | /** |
| 195 | * Generates a PHP file for a given class, interface, or namespace. |
| 196 | * |
| 197 | * @param Class_|Interface_|Namespace_ $definition The class, interface, or namespace to generate. |
| 198 | * @param string|null $namespace The namespace of the class/interface (if $definition is not already a Namespace_). |
| 199 | * @param string $outputPath The file path where the generated file should be saved. |
| 200 | * @return void |
| 201 | */ |
| 202 | public function generateFile(Class_|Interface_|Namespace_ $definition, ?string $namespace, string $outputPath): void |
| 203 | { |
| 204 | $prettyPrinter = new Standard(); |
| 205 | |
| 206 | // Convert builder to node if necessary |
| 207 | $node = $definition instanceof Namespace_ |
| 208 | ? $definition->getNode() |
| 209 | : $this->factory->namespace($namespace)->addStmt($definition)->getNode(); |
| 210 | |
| 211 | // Generate the PHP code |
| 212 | $code = $prettyPrinter->prettyPrintFile([$node]); |
| 213 | |
| 214 | // Ensure the directory exists |
| 215 | $directory = dirname($outputPath); |
| 216 | if (!is_dir($directory)) { |
| 217 | mkdir($directory, 0755, true); |
| 218 | } |
| 219 | |
| 220 | // Save the generated code to the file |
| 221 | file_put_contents($outputPath, $code); |
| 222 | } |
| 223 | } |