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