1<?php declare(strict_types=1); 2 3namespace PhpParser; 4 5use PhpParser\Builder\Class_; 6use PhpParser\Node\Identifier; 7use PhpParser\Node\Name\FullyQualified; 8use PhpParser\Node\Scalar; 9use PhpParser\Node\Stmt; 10use PhpParser\Node\Expr; 11 12class BuilderHelpersTest extends \PHPUnit\Framework\TestCase { 13 public function testNormalizeNode(): void { 14 $builder = new Class_('SomeClass'); 15 $this->assertEquals($builder->getNode(), BuilderHelpers::normalizeNode($builder)); 16 17 $attribute = new Node\Attribute(new Node\Name('Test')); 18 $this->assertSame($attribute, BuilderHelpers::normalizeNode($attribute)); 19 20 $this->expectException(\LogicException::class); 21 $this->expectExceptionMessage('Expected node or builder object'); 22 BuilderHelpers::normalizeNode('test'); 23 } 24 25 public function testNormalizeStmt(): void { 26 $stmt = new Node\Stmt\Class_('Class'); 27 $this->assertSame($stmt, BuilderHelpers::normalizeStmt($stmt)); 28 29 $expr = new Expr\Variable('fn'); 30 $normalizedExpr = BuilderHelpers::normalizeStmt($expr); 31 $this->assertEquals(new Stmt\Expression($expr), $normalizedExpr); 32 $this->assertSame($expr, $normalizedExpr->expr); 33 34 $this->expectException(\LogicException::class); 35 $this->expectExceptionMessage('Expected statement or expression node'); 36 BuilderHelpers::normalizeStmt(new Node\Attribute(new Node\Name('Test'))); 37 } 38 39 public function testNormalizeStmtInvalidType(): void { 40 $this->expectException(\LogicException::class); 41 $this->expectExceptionMessage('Expected node or builder object'); 42 BuilderHelpers::normalizeStmt('test'); 43 } 44 45 public function testNormalizeIdentifier(): void { 46 $identifier = new Node\Identifier('fn'); 47 $this->assertSame($identifier, BuilderHelpers::normalizeIdentifier($identifier)); 48 $this->assertEquals($identifier, BuilderHelpers::normalizeIdentifier('fn')); 49 50 $this->expectException(\LogicException::class); 51 $this->expectExceptionMessage('Expected string or instance of Node\Identifier'); 52 BuilderHelpers::normalizeIdentifier(1); 53 } 54 55 public function testNormalizeIdentifierOrExpr(): void { 56 $identifier = new Node\Identifier('fn'); 57 $this->assertSame($identifier, BuilderHelpers::normalizeIdentifierOrExpr($identifier)); 58 59 $expr = new Expr\Variable('fn'); 60 $this->assertSame($expr, BuilderHelpers::normalizeIdentifierOrExpr($expr)); 61 $this->assertEquals($identifier, BuilderHelpers::normalizeIdentifierOrExpr('fn')); 62 63 $this->expectException(\LogicException::class); 64 $this->expectExceptionMessage('Expected string or instance of Node\Identifier'); 65 BuilderHelpers::normalizeIdentifierOrExpr(1); 66 } 67 68 public function testNormalizeName(): void { 69 $name = new Node\Name('test'); 70 $this->assertSame($name, BuilderHelpers::normalizeName($name)); 71 $this->assertEquals( 72 new Node\Name\FullyQualified(['Namespace', 'Test']), 73 BuilderHelpers::normalizeName('\\Namespace\\Test') 74 ); 75 $this->assertEquals( 76 new Node\Name\Relative(['Test']), 77 BuilderHelpers::normalizeName('namespace\\Test') 78 ); 79 $this->assertEquals($name, BuilderHelpers::normalizeName('test')); 80 81 $this->expectException(\LogicException::class); 82 $this->expectExceptionMessage('Name cannot be empty'); 83 BuilderHelpers::normalizeName(''); 84 } 85 86 public function testNormalizeNameInvalidType(): void { 87 $this->expectException(\LogicException::class); 88 $this->expectExceptionMessage('Name must be a string or an instance of Node\Name'); 89 BuilderHelpers::normalizeName(1); 90 } 91 92 public function testNormalizeNameOrExpr(): void { 93 $expr = new Expr\Variable('fn'); 94 $this->assertSame($expr, BuilderHelpers::normalizeNameOrExpr($expr)); 95 96 $name = new Node\Name('test'); 97 $this->assertSame($name, BuilderHelpers::normalizeNameOrExpr($name)); 98 $this->assertEquals( 99 new Node\Name\FullyQualified(['Namespace', 'Test']), 100 BuilderHelpers::normalizeNameOrExpr('\\Namespace\\Test') 101 ); 102 $this->assertEquals( 103 new Node\Name\Relative(['Test']), 104 BuilderHelpers::normalizeNameOrExpr('namespace\\Test') 105 ); 106 $this->assertEquals($name, BuilderHelpers::normalizeNameOrExpr('test')); 107 108 $this->expectException(\LogicException::class); 109 $this->expectExceptionMessage('Name cannot be empty'); 110 BuilderHelpers::normalizeNameOrExpr(''); 111 } 112 113 public function testNormalizeNameOrExpInvalidType(): void { 114 $this->expectException(\LogicException::class); 115 $this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr'); 116 BuilderHelpers::normalizeNameOrExpr(1); 117 } 118 119 public function testNormalizeType(): void { 120 $this->assertEquals(new Node\Identifier('array'), BuilderHelpers::normalizeType('array')); 121 $this->assertEquals(new Node\Identifier('callable'), BuilderHelpers::normalizeType('callable')); 122 $this->assertEquals(new Node\Identifier('string'), BuilderHelpers::normalizeType('string')); 123 $this->assertEquals(new Node\Identifier('int'), BuilderHelpers::normalizeType('int')); 124 $this->assertEquals(new Node\Identifier('float'), BuilderHelpers::normalizeType('float')); 125 $this->assertEquals(new Node\Identifier('bool'), BuilderHelpers::normalizeType('bool')); 126 $this->assertEquals(new Node\Identifier('iterable'), BuilderHelpers::normalizeType('iterable')); 127 $this->assertEquals(new Node\Identifier('void'), BuilderHelpers::normalizeType('void')); 128 $this->assertEquals(new Node\Identifier('object'), BuilderHelpers::normalizeType('object')); 129 $this->assertEquals(new Node\Identifier('null'), BuilderHelpers::normalizeType('null')); 130 $this->assertEquals(new Node\Identifier('false'), BuilderHelpers::normalizeType('false')); 131 $this->assertEquals(new Node\Identifier('mixed'), BuilderHelpers::normalizeType('mixed')); 132 $this->assertEquals(new Node\Identifier('never'), BuilderHelpers::normalizeType('never')); 133 $this->assertEquals(new Node\Identifier('true'), BuilderHelpers::normalizeType('true')); 134 135 $intIdentifier = new Node\Identifier('int'); 136 $this->assertSame($intIdentifier, BuilderHelpers::normalizeType($intIdentifier)); 137 138 $intName = new Node\Name('int'); 139 $this->assertSame($intName, BuilderHelpers::normalizeType($intName)); 140 141 $intNullable = new Node\NullableType(new Identifier('int')); 142 $this->assertSame($intNullable, BuilderHelpers::normalizeType($intNullable)); 143 144 $unionType = new Node\UnionType([new Node\Identifier('int'), new Node\Identifier('string')]); 145 $this->assertSame($unionType, BuilderHelpers::normalizeType($unionType)); 146 147 $intersectionType = new Node\IntersectionType([new Node\Name('A'), new Node\Name('B')]); 148 $this->assertSame($intersectionType, BuilderHelpers::normalizeType($intersectionType)); 149 150 $expectedNullable = new Node\NullableType($intIdentifier); 151 $nullable = BuilderHelpers::normalizeType('?int'); 152 $this->assertEquals($expectedNullable, $nullable); 153 $this->assertEquals($intIdentifier, $nullable->type); 154 155 $this->expectException(\LogicException::class); 156 $this->expectExceptionMessage('Type must be a string, or an instance of Name, Identifier or ComplexType'); 157 BuilderHelpers::normalizeType(1); 158 } 159 160 public function testNormalizeTypeNullableVoid(): void { 161 $this->expectException(\LogicException::class); 162 $this->expectExceptionMessage('void type cannot be nullable'); 163 BuilderHelpers::normalizeType('?void'); 164 } 165 166 public function testNormalizeTypeNullableMixed(): void { 167 $this->expectException(\LogicException::class); 168 $this->expectExceptionMessage('mixed type cannot be nullable'); 169 BuilderHelpers::normalizeType('?mixed'); 170 } 171 172 public function testNormalizeTypeNullableNever(): void { 173 $this->expectException(\LogicException::class); 174 $this->expectExceptionMessage('never type cannot be nullable'); 175 BuilderHelpers::normalizeType('?never'); 176 } 177 178 public function testNormalizeValue(): void { 179 $expression = new Scalar\Int_(1); 180 $this->assertSame($expression, BuilderHelpers::normalizeValue($expression)); 181 182 $this->assertEquals(new Expr\ConstFetch(new Node\Name('null')), BuilderHelpers::normalizeValue(null)); 183 $this->assertEquals(new Expr\ConstFetch(new Node\Name('true')), BuilderHelpers::normalizeValue(true)); 184 $this->assertEquals(new Expr\ConstFetch(new Node\Name('false')), BuilderHelpers::normalizeValue(false)); 185 $this->assertEquals(new Scalar\Int_(2), BuilderHelpers::normalizeValue(2)); 186 $this->assertEquals(new Scalar\Float_(2.5), BuilderHelpers::normalizeValue(2.5)); 187 $this->assertEquals(new Scalar\String_('text'), BuilderHelpers::normalizeValue('text')); 188 $this->assertEquals( 189 new Expr\Array_([ 190 new Node\ArrayItem(new Scalar\Int_(0)), 191 new Node\ArrayItem(new Scalar\Int_(1), new Scalar\String_('test')), 192 ]), 193 BuilderHelpers::normalizeValue([ 194 0, 195 'test' => 1, 196 ]) 197 ); 198 199 $this->expectException(\LogicException::class); 200 $this->expectExceptionMessage('Invalid value'); 201 BuilderHelpers::normalizeValue(new \stdClass()); 202 } 203 204 public function testNormalizeDocComment(): void { 205 $docComment = new Comment\Doc('Some doc comment'); 206 $this->assertSame($docComment, BuilderHelpers::normalizeDocComment($docComment)); 207 208 $this->assertEquals($docComment, BuilderHelpers::normalizeDocComment('Some doc comment')); 209 210 $this->expectException(\LogicException::class); 211 $this->expectExceptionMessage('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); 212 BuilderHelpers::normalizeDocComment(1); 213 } 214 215 public function testNormalizeAttribute(): void { 216 $attribute = new Node\Attribute(new Node\Name('Test')); 217 $attributeGroup = new Node\AttributeGroup([$attribute]); 218 219 $this->assertEquals($attributeGroup, BuilderHelpers::normalizeAttribute($attribute)); 220 $this->assertSame($attributeGroup, BuilderHelpers::normalizeAttribute($attributeGroup)); 221 222 $this->expectException(\LogicException::class); 223 $this->expectExceptionMessage('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup'); 224 BuilderHelpers::normalizeAttribute('test'); 225 } 226 227 public function testNormalizeValueEnum() { 228 if (\PHP_VERSION_ID <= 80100) { 229 $this->markTestSkipped('Enums are supported since PHP 8.1'); 230 } 231 232 include __DIR__ . '/../fixtures/Suit.php'; 233 234 $this->assertEquals(new Expr\ClassConstFetch(new FullyQualified(\Suit::class), new Identifier('Hearts')), BuilderHelpers::normalizeValue(\Suit::Hearts)); 235 } 236} 237