Also a few other fixes of PHP class aliases spotted by phan. Bug: T353458 Change-Id: Ie79d65722c47c24f8f20f1293355cfd3c2e8c2ad
146 lines
5 KiB
PHP
146 lines
5 KiB
PHP
<?php
|
|
/**
|
|
* Copyright (C) 2012 Tamas Imrei <tamas.imrei@gmail.com> https://virtualtee.blogspot.com/
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
* @file
|
|
* @ingroup Maintenance
|
|
*/
|
|
|
|
namespace MediaWiki\Maintenance;
|
|
|
|
/**
|
|
* Doxygen filter to show correct member variable types in documentation.
|
|
*
|
|
* Based on
|
|
* <https://virtualtee.blogspot.co.uk/2012/03/tip-for-using-doxygen-for-php-code.html>
|
|
*
|
|
* It has been adapted for MediaWiki to resolve various bugs we experienced
|
|
* from using Doxygen with our coding conventions:
|
|
*
|
|
* - We want to allow documenting class members on a single line by documenting
|
|
* them as `/** @var SomeType Description here.`, and in long-form as
|
|
* `/**\n * Description here.\n * @var SomeType`.
|
|
*
|
|
* - PHP does not support native type-hinting of class members. Instead, we document
|
|
* that using `@var` in the doc blocks above it. However, Doxygen only supports
|
|
* parsing this from executable code. We achieve this by having the below filter
|
|
* take the typehint from the doc block and insert it into the source code in
|
|
* front of `$myvar`, like `protected SomeType $myvar`. This result is technically
|
|
* invalid PHP code, but Doxygen understands it this way.
|
|
*
|
|
* @internal For use by maintenance/mwdoc-filter.php
|
|
* @ingroup Maintenance
|
|
*/
|
|
class MWDoxygenFilter {
|
|
/**
|
|
* @param string $source Original source code
|
|
* @return string Filtered source code
|
|
*/
|
|
public static function filter( $source ) {
|
|
$tokens = token_get_all( $source );
|
|
$buffer = null;
|
|
$output = '';
|
|
foreach ( $tokens as $token ) {
|
|
if ( is_string( $token ) ) {
|
|
if ( $buffer !== null && $token === ';' ) {
|
|
// If we still have a buffer and the statement has ended,
|
|
// flush it and move on.
|
|
$output .= $buffer['raw'];
|
|
$buffer = null;
|
|
}
|
|
$output .= $token;
|
|
continue;
|
|
}
|
|
[ $id, $content ] = $token;
|
|
switch ( $id ) {
|
|
case T_DOC_COMMENT:
|
|
// Escape slashes so that references to namespaces are not
|
|
// wrongly interpreted as a Doxygen "\command".
|
|
$content = addcslashes( $content, '\\' );
|
|
// Look for instances of "@var SomeType".
|
|
if ( preg_match( '#@var\s+\S+#', $content ) ) {
|
|
$buffer = [ 'raw' => $content, 'desc' => null, 'type' => null, 'name' => null ];
|
|
$buffer['desc'] = preg_replace_callback(
|
|
// Strip "@var SomeType" part, but remember the type and optional name
|
|
'#@var\s+(\S+)(\s+)?(\S+)?#',
|
|
static function ( $matches ) use ( &$buffer ) {
|
|
$buffer['type'] = $matches[1];
|
|
$buffer['name'] = $matches[3] ?? null;
|
|
return ( $matches[2] ?? '' ) . ( $matches[3] ?? '' );
|
|
},
|
|
$content
|
|
);
|
|
} else {
|
|
$output .= $content;
|
|
}
|
|
break;
|
|
|
|
case T_VARIABLE:
|
|
// Doxygen requires class members to be documented in one of two ways:
|
|
//
|
|
// 1. Fully qualified:
|
|
// /** @var SomeType $name Description here. */
|
|
//
|
|
// These result in the creation of a new virtual node called $name
|
|
// with the specified type and description. The real code doesn't
|
|
// even need to exist in this case.
|
|
//
|
|
// 2. Contextual:
|
|
// /** Description here. */
|
|
// private SomeType? $name;
|
|
//
|
|
// In MediaWiki, we are mostly like #1 but without the name repeated:
|
|
// /** @var SomeType Description here. */
|
|
// private $name;
|
|
//
|
|
// These emit a warning in Doxygen because they are missing a variable name.
|
|
// Convert these to the "Contextual" kind by stripping ""@var", injecting
|
|
// type into the code, and leaving the description in-place.
|
|
if ( $buffer !== null ) {
|
|
if ( $buffer['name'] === $content ) {
|
|
// Fully qualitied "@var" comment, leave as-is.
|
|
$output .= $buffer['raw'];
|
|
$output .= $content;
|
|
} else {
|
|
// MW-style "@var" comment. Keep only the description and transplant
|
|
// the type into the code.
|
|
$output .= $buffer['desc'];
|
|
$output .= "{$buffer['type']} $content";
|
|
}
|
|
$buffer = null;
|
|
} else {
|
|
$output .= $content;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if ( $buffer !== null ) {
|
|
$buffer['raw'] .= $content;
|
|
$buffer['desc'] .= $content;
|
|
} else {
|
|
$output .= $content;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
/** @deprecated class alias since 1.43 */
|
|
class_alias( MWDoxygenFilter::class, 'MWDoxygenFilter' );
|