BuilderFactoryTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php declare(strict_types=1);
  2. namespace PhpParser;
  3. use PhpParser\Builder;
  4. use PhpParser\Node\Arg;
  5. use PhpParser\Node\Expr;
  6. use PhpParser\Node\Expr\BinaryOp\Concat;
  7. use PhpParser\Node\Identifier;
  8. use PhpParser\Node\Name;
  9. use PhpParser\Node\Scalar\LNumber;
  10. use PhpParser\Node\Scalar\String_;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Yaml\Tests\A;
  13. class BuilderFactoryTest extends TestCase
  14. {
  15. /**
  16. * @dataProvider provideTestFactory
  17. */
  18. public function testFactory($methodName, $className) {
  19. $factory = new BuilderFactory;
  20. $this->assertInstanceOf($className, $factory->$methodName('test'));
  21. }
  22. public function provideTestFactory() {
  23. return [
  24. ['namespace', Builder\Namespace_::class],
  25. ['class', Builder\Class_::class],
  26. ['interface', Builder\Interface_::class],
  27. ['trait', Builder\Trait_::class],
  28. ['method', Builder\Method::class],
  29. ['function', Builder\Function_::class],
  30. ['property', Builder\Property::class],
  31. ['param', Builder\Param::class],
  32. ['use', Builder\Use_::class],
  33. ['useFunction', Builder\Use_::class],
  34. ['useConst', Builder\Use_::class],
  35. ];
  36. }
  37. public function testVal() {
  38. // This method is a wrapper around BuilderHelpers::normalizeValue(),
  39. // which is already tested elsewhere
  40. $factory = new BuilderFactory();
  41. $this->assertEquals(
  42. new String_("foo"),
  43. $factory->val("foo")
  44. );
  45. }
  46. public function testConcat() {
  47. $factory = new BuilderFactory();
  48. $varA = new Expr\Variable('a');
  49. $varB = new Expr\Variable('b');
  50. $varC = new Expr\Variable('c');
  51. $this->assertEquals(
  52. new Concat($varA, $varB),
  53. $factory->concat($varA, $varB)
  54. );
  55. $this->assertEquals(
  56. new Concat(new Concat($varA, $varB), $varC),
  57. $factory->concat($varA, $varB, $varC)
  58. );
  59. $this->assertEquals(
  60. new Concat(new Concat(new String_("a"), $varB), new String_("c")),
  61. $factory->concat("a", $varB, "c")
  62. );
  63. }
  64. public function testConcatOneError() {
  65. $this->expectException(\LogicException::class);
  66. $this->expectExceptionMessage('Expected at least two expressions');
  67. (new BuilderFactory())->concat("a");
  68. }
  69. public function testConcatInvalidExpr() {
  70. $this->expectException(\LogicException::class);
  71. $this->expectExceptionMessage('Expected string or Expr');
  72. (new BuilderFactory())->concat("a", 42);
  73. }
  74. public function testArgs() {
  75. $factory = new BuilderFactory();
  76. $unpack = new Arg(new Expr\Variable('c'), false, true);
  77. $this->assertEquals(
  78. [
  79. new Arg(new Expr\Variable('a')),
  80. new Arg(new String_('b')),
  81. $unpack
  82. ],
  83. $factory->args([new Expr\Variable('a'), 'b', $unpack])
  84. );
  85. }
  86. public function testCalls() {
  87. $factory = new BuilderFactory();
  88. // Simple function call
  89. $this->assertEquals(
  90. new Expr\FuncCall(
  91. new Name('var_dump'),
  92. [new Arg(new String_('str'))]
  93. ),
  94. $factory->funcCall('var_dump', ['str'])
  95. );
  96. // Dynamic function call
  97. $this->assertEquals(
  98. new Expr\FuncCall(new Expr\Variable('fn')),
  99. $factory->funcCall(new Expr\Variable('fn'))
  100. );
  101. // Simple method call
  102. $this->assertEquals(
  103. new Expr\MethodCall(
  104. new Expr\Variable('obj'),
  105. new Identifier('method'),
  106. [new Arg(new LNumber(42))]
  107. ),
  108. $factory->methodCall(new Expr\Variable('obj'), 'method', [42])
  109. );
  110. // Explicitly pass Identifier node
  111. $this->assertEquals(
  112. new Expr\MethodCall(
  113. new Expr\Variable('obj'),
  114. new Identifier('method')
  115. ),
  116. $factory->methodCall(new Expr\Variable('obj'), new Identifier('method'))
  117. );
  118. // Dynamic method call
  119. $this->assertEquals(
  120. new Expr\MethodCall(
  121. new Expr\Variable('obj'),
  122. new Expr\Variable('method')
  123. ),
  124. $factory->methodCall(new Expr\Variable('obj'), new Expr\Variable('method'))
  125. );
  126. // Simple static method call
  127. $this->assertEquals(
  128. new Expr\StaticCall(
  129. new Name\FullyQualified('Foo'),
  130. new Identifier('bar'),
  131. [new Arg(new Expr\Variable('baz'))]
  132. ),
  133. $factory->staticCall('\Foo', 'bar', [new Expr\Variable('baz')])
  134. );
  135. // Dynamic static method call
  136. $this->assertEquals(
  137. new Expr\StaticCall(
  138. new Expr\Variable('foo'),
  139. new Expr\Variable('bar')
  140. ),
  141. $factory->staticCall(new Expr\Variable('foo'), new Expr\Variable('bar'))
  142. );
  143. // Simple new call
  144. $this->assertEquals(
  145. new Expr\New_(new Name\FullyQualified('stdClass')),
  146. $factory->new('\stdClass')
  147. );
  148. // Dynamic new call
  149. $this->assertEquals(
  150. new Expr\New_(
  151. new Expr\Variable('foo'),
  152. [new Arg(new String_('bar'))]
  153. ),
  154. $factory->new(new Expr\Variable('foo'), ['bar'])
  155. );
  156. }
  157. public function testConstFetches() {
  158. $factory = new BuilderFactory();
  159. $this->assertEquals(
  160. new Expr\ConstFetch(new Name('FOO')),
  161. $factory->constFetch('FOO')
  162. );
  163. $this->assertEquals(
  164. new Expr\ClassConstFetch(new Name('Foo'), new Identifier('BAR')),
  165. $factory->classConstFetch('Foo', 'BAR')
  166. );
  167. $this->assertEquals(
  168. new Expr\ClassConstFetch(new Expr\Variable('foo'), new Identifier('BAR')),
  169. $factory->classConstFetch(new Expr\Variable('foo'), 'BAR')
  170. );
  171. }
  172. public function testVar() {
  173. $factory = new BuilderFactory();
  174. $this->assertEquals(
  175. new Expr\Variable("foo"),
  176. $factory->var("foo")
  177. );
  178. $this->assertEquals(
  179. new Expr\Variable(new Expr\Variable("foo")),
  180. $factory->var($factory->var("foo"))
  181. );
  182. }
  183. public function testPropertyFetch() {
  184. $f = new BuilderFactory();
  185. $this->assertEquals(
  186. new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'),
  187. $f->propertyFetch($f->var('foo'), 'bar')
  188. );
  189. $this->assertEquals(
  190. new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'),
  191. $f->propertyFetch($f->var('foo'), new Identifier('bar'))
  192. );
  193. $this->assertEquals(
  194. new Expr\PropertyFetch(new Expr\Variable('foo'), new Expr\Variable('bar')),
  195. $f->propertyFetch($f->var('foo'), $f->var('bar'))
  196. );
  197. }
  198. public function testInvalidIdentifier() {
  199. $this->expectException(\LogicException::class);
  200. $this->expectExceptionMessage('Expected string or instance of Node\Identifier');
  201. (new BuilderFactory())->classConstFetch('Foo', new Expr\Variable('foo'));
  202. }
  203. public function testInvalidIdentifierOrExpr() {
  204. $this->expectException(\LogicException::class);
  205. $this->expectExceptionMessage('Expected string or instance of Node\Identifier or Node\Expr');
  206. (new BuilderFactory())->staticCall('Foo', new Name('bar'));
  207. }
  208. public function testInvalidNameOrExpr() {
  209. $this->expectException(\LogicException::class);
  210. $this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr');
  211. (new BuilderFactory())->funcCall(new Node\Stmt\Return_());
  212. }
  213. public function testInvalidVar() {
  214. $this->expectException(\LogicException::class);
  215. $this->expectExceptionMessage('Variable name must be string or Expr');
  216. (new BuilderFactory())->var(new Node\Stmt\Return_());
  217. }
  218. public function testIntegration() {
  219. $factory = new BuilderFactory;
  220. $node = $factory->namespace('Name\Space')
  221. ->addStmt($factory->use('Foo\Bar\SomeOtherClass'))
  222. ->addStmt($factory->use('Foo\Bar')->as('A'))
  223. ->addStmt($factory->useFunction('strlen'))
  224. ->addStmt($factory->useConst('PHP_VERSION'))
  225. ->addStmt($factory
  226. ->class('SomeClass')
  227. ->extend('SomeOtherClass')
  228. ->implement('A\Few', '\Interfaces')
  229. ->makeAbstract()
  230. ->addStmt($factory->useTrait('FirstTrait'))
  231. ->addStmt($factory->useTrait('SecondTrait', 'ThirdTrait')
  232. ->and('AnotherTrait')
  233. ->with($factory->traitUseAdaptation('foo')->as('bar'))
  234. ->with($factory->traitUseAdaptation('AnotherTrait', 'baz')->as('test'))
  235. ->with($factory->traitUseAdaptation('AnotherTrait', 'func')->insteadof('SecondTrait')))
  236. ->addStmt($factory->method('firstMethod'))
  237. ->addStmt($factory->method('someMethod')
  238. ->makePublic()
  239. ->makeAbstract()
  240. ->addParam($factory->param('someParam')->setType('SomeClass'))
  241. ->setDocComment('/**
  242. * This method does something.
  243. *
  244. * @param SomeClass And takes a parameter
  245. */'))
  246. ->addStmt($factory->method('anotherMethod')
  247. ->makeProtected()
  248. ->addParam($factory->param('someParam')->setDefault('test'))
  249. ->addStmt(new Expr\Print_(new Expr\Variable('someParam'))))
  250. ->addStmt($factory->property('someProperty')->makeProtected())
  251. ->addStmt($factory->property('anotherProperty')
  252. ->makePrivate()
  253. ->setDefault([1, 2, 3])))
  254. ->getNode()
  255. ;
  256. $expected = <<<'EOC'
  257. <?php
  258. namespace Name\Space;
  259. use Foo\Bar\SomeOtherClass;
  260. use Foo\Bar as A;
  261. use function strlen;
  262. use const PHP_VERSION;
  263. abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
  264. {
  265. use FirstTrait;
  266. use SecondTrait, ThirdTrait, AnotherTrait {
  267. foo as bar;
  268. AnotherTrait::baz as test;
  269. AnotherTrait::func insteadof SecondTrait;
  270. }
  271. protected $someProperty;
  272. private $anotherProperty = array(1, 2, 3);
  273. function firstMethod()
  274. {
  275. }
  276. /**
  277. * This method does something.
  278. *
  279. * @param SomeClass And takes a parameter
  280. */
  281. public abstract function someMethod(SomeClass $someParam);
  282. protected function anotherMethod($someParam = 'test')
  283. {
  284. print $someParam;
  285. }
  286. }
  287. EOC;
  288. $stmts = [$node];
  289. $prettyPrinter = new PrettyPrinter\Standard();
  290. $generated = $prettyPrinter->prettyPrintFile($stmts);
  291. $this->assertEquals(
  292. str_replace("\r\n", "\n", $expected),
  293. str_replace("\r\n", "\n", $generated)
  294. );
  295. }
  296. }