MediaWiki master
MWDoxygenFilter.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Maintenance;
25
49class MWDoxygenFilter {
54 public static function filter( $source ) {
55 $tokens = token_get_all( $source );
56 $buffer = null;
57 $output = '';
58 foreach ( $tokens as $token ) {
59 if ( is_string( $token ) ) {
60 if ( $buffer !== null && $token === ';' ) {
61 // If we still have a buffer and the statement has ended,
62 // flush it and move on.
63 $output .= $buffer['raw'];
64 $buffer = null;
65 }
66 $output .= $token;
67 continue;
68 }
69 [ $id, $content ] = $token;
70 switch ( $id ) {
71 case T_DOC_COMMENT:
72 // Escape slashes so that references to namespaces are not
73 // wrongly interpreted as a Doxygen "\command".
74 $content = addcslashes( $content, '\\' );
75 // Look for instances of "@var SomeType".
76 if ( preg_match( '#@var\s+\S+#', $content ) ) {
77 $buffer = [ 'raw' => $content, 'desc' => null, 'type' => null, 'name' => null ];
78 $buffer['desc'] = preg_replace_callback(
79 // Strip "@var SomeType" part, but remember the type and optional name
80 '#@var\s+(\S+)(\s+)?(\S+)?#',
81 static function ( $matches ) use ( &$buffer ) {
82 $buffer['type'] = $matches[1];
83 $buffer['name'] = $matches[3] ?? null;
84 return ( $matches[2] ?? '' ) . ( $matches[3] ?? '' );
85 },
86 $content
87 );
88 } else {
89 $output .= $content;
90 }
91 break;
92
93 case T_VARIABLE:
94 // Doxygen requires class members to be documented in one of two ways:
95 //
96 // 1. Fully qualified:
97 // /** @var SomeType $name Description here. */
98 //
99 // These result in the creation of a new virtual node called $name
100 // with the specified type and description. The real code doesn't
101 // even need to exist in this case.
102 //
103 // 2. Contextual:
104 // /** Description here. */
105 // private SomeType? $name;
106 //
107 // In MediaWiki, we are mostly like #1 but without the name repeated:
108 // /** @var SomeType Description here. */
109 // private $name;
110 //
111 // These emit a warning in Doxygen because they are missing a variable name.
112 // Convert these to the "Contextual" kind by stripping ""@var", injecting
113 // type into the code, and leaving the description in-place.
114 if ( $buffer !== null ) {
115 if ( $buffer['name'] === $content ) {
116 // Fully qualitied "@var" comment, leave as-is.
117 $output .= $buffer['raw'];
118 $output .= $content;
119 } else {
120 // MW-style "@var" comment. Keep only the description and transplant
121 // the type into the code.
122 $output .= $buffer['desc'];
123 $output .= "{$buffer['type']} $content";
124 }
125 $buffer = null;
126 } else {
127 $output .= $content;
128 }
129 break;
130
131 default:
132 if ( $buffer !== null ) {
133 $buffer['raw'] .= $content;
134 $buffer['desc'] .= $content;
135 } else {
136 $output .= $content;
137 }
138 break;
139 }
140 }
141 return $output;
142 }
143}
144
146class_alias( MWDoxygenFilter::class, 'MWDoxygenFilter' );
$source