docs: Factor out MWDoxygenFilter from mwdoc-filter.php with tests
authorTimo Tijhof <krinklemail@gmail.com>
Thu, 5 Sep 2019 16:58:07 +0000 (17:58 +0100)
committerTimo Tijhof <krinklemail@gmail.com>
Thu, 5 Sep 2019 17:22:23 +0000 (18:22 +0100)
Bug: T232104
Change-Id: I591f44678cff4adb8863ce5c6ce231a8d3e162d7

autoload.php
maintenance/includes/MWDoxygenFilter.php [new file with mode: 0644]
maintenance/mwdoc-filter.php
tests/phpunit/maintenance/MWDoxygenFilterTest.php [new file with mode: 0644]

index eb54f7c..0255a23 100644 (file)
@@ -829,6 +829,7 @@ $wgAutoloadLocalClasses = [
        'MWCryptRand' => __DIR__ . '/includes/utils/MWCryptRand.php',
        'MWDebug' => __DIR__ . '/includes/debug/MWDebug.php',
        'MWDocGen' => __DIR__ . '/maintenance/mwdocgen.php',
+       'MWDoxygenFilter' => __DIR__ . '/maintenance/includes/MWDoxygenFilter.php',
        'MWException' => __DIR__ . '/includes/exception/MWException.php',
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
        'MWExceptionRenderer' => __DIR__ . '/includes/exception/MWExceptionRenderer.php',
diff --git a/maintenance/includes/MWDoxygenFilter.php b/maintenance/includes/MWDoxygenFilter.php
new file mode 100644 (file)
index 0000000..30cb97d
--- /dev/null
@@ -0,0 +1,107 @@
+<?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
+ * @phan-file-suppress PhanInvalidCommentForDeclarationType False negative about about `@var`
+ * @phan-file-suppress PhanUnextractableAnnotation False negative about about `@var`
+ */
+
+/**
+ * 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>
+ *
+ * Improved to resolve various bugs and better MediaWiki PHPDoc conventions:
+ *
+ * - Insert variable name after typehint instead of at end of line so that
+ *   documentation text may follow after `@var Type`.
+ * - Insert typehint into source code before $variable instead of inside the comment
+ *   so that Doxygen interprets it.
+ * - Strip the text after `@var` from the output to avoid Doxygen warnings about bogus
+ *   symbols being documented but not declared or defined.
+ *
+ * @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 = $bufferType = 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;
+                                       $buffer = $bufferType = null;
+                               }
+                               $output .= $token;
+                               continue;
+                       }
+                       list( $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 Type" not followed by $name.
+                                       if ( preg_match( '#@var\s+([^\s]+)\s+([^\$]+)#s', $content ) ) {
+                                               $buffer = preg_replace_callback(
+                                                       // Strip the "@var Type" part and remember the type
+                                                       '#(@var\s+)([^\s]+)#s',
+                                                       function ( $matches ) use ( &$bufferType ) {
+                                                               $bufferType = $matches[2];
+                                                               return '';
+                                                       },
+                                                       $content
+                                               );
+                                       } else {
+                                               $output .= $content;
+                                       }
+                                       break;
+
+                               case T_VARIABLE:
+                                       if ( $buffer !== null ) {
+                                               $output .= $buffer;
+                                               $output .= "$bufferType $content";
+                                               $buffer = $bufferType = null;
+                                       } else {
+                                               $output .= $content;
+                                       }
+                                       break;
+
+                               default:
+                                       if ( $buffer !== null ) {
+                                               $buffer .= $content;
+                                       } else {
+                                               $output .= $content;
+                                       }
+                                       break;
+                       }
+               }
+               return $output;
+       }
+}
index 1da805e..cabf489 100644 (file)
@@ -1,22 +1,9 @@
 <?php
 /**
- * Doxygen filter to show correct member variable types in documentation.
+ * Filter for PHP source code that allows Doxygen to understand it better.
  *
- * Should be set in Doxygen INPUT_FILTER as "php mwdoc-filter.php"
- *
- * Based on
- * <https://virtualtee.blogspot.co.uk/2012/03/tip-for-using-doxygen-for-php-code.html>
- *
- * Improved to resolve various bugs and better MediaWiki PHPDoc conventions:
- *
- * - Insert variable name after typehint instead of at end of line so that
- *   documentation text may follow after "@var Type".
- * - Insert typehint into source code before $variable instead of inside the comment
- *   so that Doxygen interprets it.
- * - Strip the text after @var from the output to avoid Doxygen warnings aboug bogus
- *   symbols being documented but not declared or defined.
- *
- * Copyright (C) 2012 Tamas Imrei <tamas.imrei@gmail.com> https://virtualtee.blogspot.com/
+ * This CLI script is intended to be configured as the INPUT_FILTER
+ * script in a Doxyfile, e.g. like "php mwdoc-filter.php".
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the "Software"),
@@ -42,59 +29,7 @@ if ( PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg' ) {
        die( "This filter can only be run from the command line.\n" );
 }
 
-$source = file_get_contents( $argv[1] );
-$tokens = token_get_all( $source );
+require_once __DIR__ . '/includes/MWDoxygenFilter.php';
 
-$buffer = $bufferType = null;
-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.
-                       echo $buffer;
-                       $buffer = $bufferType = null;
-               }
-               echo $token;
-               continue;
-       }
-       list( $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 Type" not followed by $name.
-                       if ( preg_match( '#@var\s+([^\s]+)\s+([^\$]+)#s', $content ) ) {
-                               $buffer = preg_replace_callback(
-                                       // Strip the "@var Type" part and remember the type
-                                       '#(@var\s+)([^\s]+)#s',
-                                       function ( $matches ) use ( &$bufferType ) {
-                                               $bufferType = $matches[2];
-                                               return '';
-                                       },
-                                       $content
-                               );
-                       } else {
-                               echo $content;
-                       }
-                       break;
-
-               case T_VARIABLE:
-                       if ( $buffer !== null ) {
-                               echo $buffer;
-                               echo "$bufferType $content";
-                               $buffer = $bufferType = null;
-                       } else {
-                               echo $content;
-                       }
-                       break;
-
-               default:
-                       if ( $buffer !== null ) {
-                               $buffer .= $content;
-                       } else {
-                               echo $content;
-                       }
-                       break;
-       }
-}
+$source = file_get_contents( $argv[1] );
+echo MWDoxygenFilter::filter( $source );
diff --git a/tests/phpunit/maintenance/MWDoxygenFilterTest.php b/tests/phpunit/maintenance/MWDoxygenFilterTest.php
new file mode 100644 (file)
index 0000000..2e06d6f
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+
+/**
+* @covers MWDoxygenFilter
+ */
+class MWDoxygenFilterTest extends \PHPUnit\Framework\TestCase {
+
+       public static function provideFilter() {
+               yield 'No @var' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /** Some Words here */
+       protected $name;
+}
+CODE
+               ];
+
+               yield 'One-line var with type' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /** @var SomeType */
+       protected $name;
+}
+CODE
+                       , <<<'CODE'
+<?php class MyClass {
+       /**  */
+       protected SomeType $name;
+}
+CODE
+               ];
+
+               yield 'One-line var with type and description' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /** @var SomeType Some description */
+       protected $name;
+}
+CODE
+                       , <<<'CODE'
+<?php class MyClass {
+       /**  Some description */
+       protected SomeType $name;
+}
+CODE
+               ];
+
+               yield 'One-line var with type, name, and description' => [
+                       // In this full form, Doxygen understands it just fine.
+                       // No changes made.
+                       <<<'CODE'
+<?php class MyClass {
+       /** @var SomeType $name Some description */
+       protected $name;
+}
+CODE
+               ];
+
+               yield 'Multi-line var with type' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /**
+        * @var SomeType
+        */
+       protected $name;
+}
+CODE
+                       , <<<'CODE'
+<?php class MyClass {
+       /**
+        * 
+        */
+       protected SomeType $name;
+}
+CODE
+               ];
+
+               yield 'Multi-line var with type and description' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /**
+        * Some description
+        * @var SomeType
+        */
+       protected $name;
+}
+CODE
+                       , <<<'CODE'
+<?php class MyClass {
+       /**
+        * Some description
+        * 
+        */
+       protected SomeType $name;
+}
+CODE
+               ];
+
+               yield 'Multi-line var with type, name, and description' => [
+                       <<<'CODE'
+<?php class MyClass {
+       /**
+        * Some description
+        * @var SomeType $name
+        */
+       protected $name;
+}
+CODE
+                       , <<<'CODE'
+<?php class MyClass {
+       /**
+        * Some description
+        * @var SomeType $name
+        */
+       protected $name;
+}
+CODE
+               ];
+       }
+
+       /**
+        * @dataProvider provideFilter
+        */
+       public function testFilter( $source, $expected = null ) {
+               if ( $expected === null ) {
+                       $expected = $source;
+               }
+               $this->assertSame( $expected, MWDoxygenFilter::filter( $source ), 'Source code' );
+       }
+}