MediaWiki  master
MWDoxygenFilter.php
Go to the documentation of this file.
1 <?php
47 class 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 }
$matches
$source
$content
Definition: router.php:76