3030final class FrameworkCodeTest extends TestCase
3131{
3232 /**
33- * Cache of discovered test class names.
33+ * Cache of source filenames.
34+ *
35+ * @var list<non-empty-string>
36+ */
37+ private static array $ sourceFiles = [];
38+
39+ /**
40+ * Cache of test class names.
41+ *
42+ * @var list<class-string>
3443 */
3544 private static array $ testClasses = [];
3645
46+ /**
47+ * @var list<string>
48+ */
3749 private static array $ recognizedGroupAttributeNames = [
3850 'AutoReview ' ,
3951 'CacheLive ' ,
@@ -42,6 +54,42 @@ final class FrameworkCodeTest extends TestCase
4254 'SeparateProcess ' ,
4355 ];
4456
57+ public function testDeprecationsAreProperlyVersioned (): void
58+ {
59+ $ deprecationsWithoutVersion = [];
60+
61+ foreach ($ this ->getSourceFiles () as $ file ) {
62+ $ lines = file ($ file , FILE_IGNORE_NEW_LINES );
63+
64+ if ($ lines === false ) {
65+ continue ;
66+ }
67+
68+ foreach ($ lines as $ number => $ line ) {
69+ if (! str_contains ($ line , '@deprecated ' )) {
70+ continue ;
71+ }
72+
73+ if (preg_match ('/((?:\/\*)?\*|\/\/)\s+@deprecated\s+(?P<text>.+?)(?:\s*\*\s*)?$/ ' , $ line , $ matches ) === 1 ) {
74+ $ deprecationText = trim ($ matches ['text ' ]);
75+
76+ if (preg_match ('/^v?\d+\.\d+/ ' , $ deprecationText ) !== 1 ) {
77+ $ deprecationsWithoutVersion [] = sprintf ('%s:%d ' , $ file , ++$ number );
78+ }
79+ }
80+ }
81+ }
82+
83+ $ this ->assertCount (
84+ 0 ,
85+ $ deprecationsWithoutVersion ,
86+ sprintf (
87+ "The following lines contain @deprecated annotations without a version number: \n%s " ,
88+ implode ("\n" , array_map (static fn (string $ location ): string => " * {$ location }" , $ deprecationsWithoutVersion )),
89+ ),
90+ );
91+ }
92+
4593 /**
4694 * @param class-string $class
4795 */
@@ -87,14 +135,16 @@ public static function provideEachTestClassHasCorrectGroupAttributeName(): itera
87135 }
88136 }
89137
138+ /**
139+ * @return list<class-string>
140+ */
90141 private static function getTestClasses (): array
91142 {
92143 if (self ::$ testClasses !== []) {
93144 return self ::$ testClasses ;
94145 }
95146
96147 helper ('filesystem ' );
97-
98148 $ directory = set_realpath (dirname (__DIR__ ), true );
99149
100150 $ iterator = new RecursiveIteratorIterator (
@@ -145,4 +195,41 @@ static function (SplFileInfo $file) use ($directory): string {
145195
146196 return $ testClasses ;
147197 }
198+
199+ /**
200+ * @return list<string>
201+ */
202+ private function getSourceFiles (): array
203+ {
204+ if (self ::$ sourceFiles !== []) {
205+ return self ::$ sourceFiles ;
206+ }
207+
208+ helper ('filesystem ' );
209+ $ phpFiles = [];
210+ $ basePath = dirname (__DIR__ , 3 );
211+
212+ foreach (['system ' , 'app ' , 'tests ' ] as $ dir ) {
213+ $ directory = set_realpath ($ basePath . DIRECTORY_SEPARATOR . $ dir , true );
214+
215+ $ iterator = new RecursiveIteratorIterator (
216+ new RecursiveDirectoryIterator (
217+ $ directory ,
218+ FilesystemIterator::SKIP_DOTS ,
219+ ),
220+ RecursiveIteratorIterator::CHILD_FIRST ,
221+ );
222+
223+ /** @var SplFileInfo $file */
224+ foreach ($ iterator as $ file ) {
225+ if ($ file ->isFile () && str_ends_with ($ file ->getPathname (), '.php ' )) {
226+ $ phpFiles [] = $ file ->getRealPath ();
227+ }
228+ }
229+ }
230+
231+ self ::$ sourceFiles = $ phpFiles ;
232+
233+ return $ phpFiles ;
234+ }
148235}
0 commit comments