ValidClassNamePassTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /*
  3. * This file is part of Psy Shell.
  4. *
  5. * (c) 2012-2017 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Psy\Test\CodeCleaner;
  11. use Psy\CodeCleaner\ValidClassNamePass;
  12. use Psy\Exception\Exception;
  13. class ValidClassNamePassTest extends CodeCleanerTestCase
  14. {
  15. public function setUp()
  16. {
  17. $this->setPass(new ValidClassNamePass());
  18. }
  19. /**
  20. * @dataProvider getInvalid
  21. */
  22. public function testProcessInvalid($code, $php54 = false)
  23. {
  24. try {
  25. $stmts = $this->parse($code);
  26. $this->traverse($stmts);
  27. $this->fail();
  28. } catch (Exception $e) {
  29. if ($php54 && version_compare(PHP_VERSION, '5.4', '<')) {
  30. $this->assertInstanceOf('Psy\Exception\ParseErrorException', $e);
  31. } else {
  32. $this->assertInstanceOf('Psy\Exception\FatalErrorException', $e);
  33. }
  34. }
  35. }
  36. public function getInvalid()
  37. {
  38. // class declarations
  39. return array(
  40. // core class
  41. array('class stdClass {}'),
  42. // capitalization
  43. array('class stdClass {}'),
  44. // collisions with interfaces and traits
  45. array('interface stdClass {}'),
  46. array('trait stdClass {}', true),
  47. // collisions inside the same code snippet
  48. array('
  49. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  50. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  51. '),
  52. array('
  53. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  54. trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  55. ', true),
  56. array('
  57. trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  58. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  59. ', true),
  60. array('
  61. trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  62. interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  63. ', true),
  64. array('
  65. interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  66. trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  67. ', true),
  68. array('
  69. interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  70. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  71. '),
  72. array('
  73. class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  74. interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {}
  75. '),
  76. // namespaced collisions
  77. array('
  78. namespace Psy\\Test\\CodeCleaner {
  79. class ValidClassNamePassTest {}
  80. }
  81. '),
  82. array('
  83. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  84. class Beta {}
  85. }
  86. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  87. class Beta {}
  88. }
  89. '),
  90. // extends and implements
  91. array('class ValidClassNamePassTest extends NotAClass {}'),
  92. array('class ValidClassNamePassTest extends ArrayAccess {}'),
  93. array('class ValidClassNamePassTest implements stdClass {}'),
  94. array('class ValidClassNamePassTest implements ArrayAccess, stdClass {}'),
  95. array('interface ValidClassNamePassTest extends stdClass {}'),
  96. array('interface ValidClassNamePassTest extends ArrayAccess, stdClass {}'),
  97. // class instantiations
  98. array('new Psy_Test_CodeCleaner_ValidClassNamePass_Gamma();'),
  99. array('
  100. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  101. new Psy_Test_CodeCleaner_ValidClassNamePass_Delta();
  102. }
  103. '),
  104. // class constant fetch
  105. array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::FOO'),
  106. // static call
  107. array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::foo()'),
  108. array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::$foo()'),
  109. );
  110. }
  111. /**
  112. * @dataProvider getValid
  113. */
  114. public function testProcessValid($code)
  115. {
  116. $stmts = $this->parse($code);
  117. $this->traverse($stmts);
  118. }
  119. public function getValid()
  120. {
  121. $valid = array(
  122. // class declarations
  123. array('class Psy_Test_CodeCleaner_ValidClassNamePass_Epsilon {}'),
  124. array('namespace Psy\Test\CodeCleaner\ValidClassNamePass; class Zeta {}'),
  125. array('
  126. namespace { class Psy_Test_CodeCleaner_ValidClassNamePass_Eta {}; }
  127. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  128. class Psy_Test_CodeCleaner_ValidClassNamePass_Eta {}
  129. }
  130. '),
  131. array('namespace Psy\Test\CodeCleaner\ValidClassNamePass { class stdClass {} }'),
  132. // class instantiations
  133. array('new stdClass();'),
  134. array('new stdClass();'),
  135. array('
  136. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  137. class Theta {}
  138. }
  139. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  140. new Theta();
  141. }
  142. '),
  143. array('
  144. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  145. class Iota {}
  146. new Iota();
  147. }
  148. '),
  149. array('
  150. namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass {
  151. class Kappa {}
  152. }
  153. namespace {
  154. new \\Psy\\Test\\CodeCleaner\\ValidClassNamePass\\Kappa();
  155. }
  156. '),
  157. // Class constant fetch (ValidConstantPassTest validates the actual constant)
  158. array('class A {} A::FOO'),
  159. array('$a = new DateTime; $a::ATOM'),
  160. array('interface A { const B = 1; } A::B'),
  161. // static call
  162. array('DateTime::createFromFormat()'),
  163. array('DateTime::$someMethod()'),
  164. array('Psy\Test\CodeCleaner\Fixtures\ClassWithStatic::doStuff()'),
  165. array('Psy\Test\CodeCleaner\Fixtures\ClassWithCallStatic::doStuff()'),
  166. // Allow `self` and `static` as class names.
  167. array('
  168. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  169. public static function getInstance() {
  170. return new self();
  171. }
  172. }
  173. '),
  174. array('
  175. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  176. public static function getInstance() {
  177. return new SELF();
  178. }
  179. }
  180. '),
  181. array('
  182. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  183. public static function getInstance() {
  184. return new self;
  185. }
  186. }
  187. '),
  188. array('
  189. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  190. public static function getInstance() {
  191. return new static();
  192. }
  193. }
  194. '),
  195. array('
  196. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  197. public static function getInstance() {
  198. return new Static();
  199. }
  200. }
  201. '),
  202. array('
  203. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  204. public static function getInstance() {
  205. return new static;
  206. }
  207. }
  208. '),
  209. array('
  210. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  211. public static function foo() {
  212. return parent::bar();
  213. }
  214. }
  215. '),
  216. array('
  217. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  218. public static function foo() {
  219. return self::bar();
  220. }
  221. }
  222. '),
  223. array('
  224. class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic {
  225. public static function foo() {
  226. return static::bar();
  227. }
  228. }
  229. '),
  230. array('class A { static function b() { return new A; } }'),
  231. array('
  232. class A {
  233. const B = 123;
  234. function c() {
  235. return A::B;
  236. }
  237. }
  238. '),
  239. array('class A {} class B { function c() { return new A; } }'),
  240. // recursion
  241. array('class A { function a() { A::a(); } }'),
  242. // conditionally defined classes
  243. array('
  244. class A {}
  245. if (false) {
  246. class A {}
  247. }
  248. '),
  249. array('
  250. class A {}
  251. if (true) {
  252. class A {}
  253. } else if (false) {
  254. class A {}
  255. } else {
  256. class A {}
  257. }
  258. '),
  259. // ewww
  260. array('
  261. class A {}
  262. if (true):
  263. class A {}
  264. elseif (false):
  265. class A {}
  266. else:
  267. class A {}
  268. endif;
  269. '),
  270. array('
  271. class A {}
  272. while (false) { class A {} }
  273. '),
  274. array('
  275. class A {}
  276. do { class A {} } while (false);
  277. '),
  278. array('
  279. class A {}
  280. switch (1) {
  281. case 0:
  282. class A {}
  283. break;
  284. case 1:
  285. class A {}
  286. break;
  287. case 2:
  288. class A {}
  289. break;
  290. }
  291. '),
  292. );
  293. // Ugh. There's gotta be a better way to test for this.
  294. if (class_exists('PhpParser\ParserFactory')) {
  295. // PHP 7.0 anonymous classes, only supported by PHP Parser v2.x
  296. $valid[] = array('$obj = new class() {}');
  297. }
  298. if (version_compare(PHP_VERSION, '5.5', '>=')) {
  299. $valid[] = array('interface A {} A::class');
  300. $valid[] = array('interface A {} A::CLASS');
  301. $valid[] = array('class A {} A::class');
  302. $valid[] = array('class A {} A::CLASS');
  303. $valid[] = array('A::class');
  304. $valid[] = array('A::CLASS');
  305. }
  306. return $valid;
  307. }
  308. }