feat: more readable phpdoc escaping

pull/11208/head
Brent Shaffer 2 years ago
parent 37cae2d2e1
commit 7e2ad0521b
  1. 22
      php/tests/GeneratedClassTest.php
  2. 17
      php/tests/proto/test_special_characters.proto
  3. 31
      src/google/protobuf/compiler/php/php_generator.cc

@ -13,6 +13,7 @@ use Foo\Test32Fields;
use Foo\TestEnum; use Foo\TestEnum;
use Foo\TestIncludeNamespaceMessage; use Foo\TestIncludeNamespaceMessage;
use Foo\TestIncludePrefixMessage; use Foo\TestIncludePrefixMessage;
use Foo\TestSpecialCharacters;
use Foo\TestMessage; use Foo\TestMessage;
use Foo\TestMessage\Sub; use Foo\TestMessage\Sub;
use Foo\TestMessage_Sub; use Foo\TestMessage_Sub;
@ -1894,4 +1895,25 @@ class GeneratedClassTest extends TestBase
$m->setVersion('1'); $m->setVersion('1');
$this->assertEquals(8, $m->getId()); $this->assertEquals(8, $m->getId());
} }
public function testSpecialCharacters()
{
$reflectionMethod = new \ReflectionMethod(TestSpecialCharacters::class, 'getA');
$docComment = $reflectionMethod->getDocComment();
$commentLines = explode("\n", $docComment);
$this->assertEquals('/**', array_shift($commentLines));
$this->assertEquals(' */', array_pop($commentLines));
$docComment = implode("\n", $commentLines);
// test special characters
$this->assertContains(";,/?:&=+$-_.!~*'()", $docComment);
// test open doc comment
$this->assertContains('/*', $docComment);
// test escaped closed doc comment
$this->assertNotContains('*/', $docComment);
$this->assertContains('{@*}', $docComment);
// test escaped at-sign
$this->assertContains('\@foo', $docComment);
// test forwardslash on new line
$this->assertContains("* /\n", $docComment);
}
} }

@ -0,0 +1,17 @@
syntax = "proto3";
package foo;
message TestSpecialCharacters {
// test special characters (shouldn't escape): ;,/?:&=+$-_.!~*'()
// test open comment (shouldn't escape): /*
// test close comment (should escape): */
// test at-sign (should escape): @foo
// test forward slash as first character on a newline:
///
string a = 1;
///
// test forward slash as first character on first line
string b = 2;
}

@ -1610,38 +1610,37 @@ static std::string EscapePhpdoc(absl::string_view input) {
std::string result; std::string result;
result.reserve(input.size() * 2); result.reserve(input.size() * 2);
char prev = '*'; char prev = '\0';
for (std::string::size_type i = 0; i < input.size(); i++) { for (std::string::size_type i = 0; i < input.size(); i++) {
char c = input[i]; char c = input[i];
switch (c) { switch (c) {
case '*': // "/*" is allowed, do nothing
// Avoid "/*". // case '*':
if (prev == '/') { // if (prev == '/') {
result.append("&#42;"); // result.append("&#42;");
} else { // } else {
result.push_back(c); // result.push_back(c);
} // }
break; // break;
case '/': case '/':
// Avoid "*/". // Escape "*/" with "{@*}".
if (prev == '*') { if (prev == '*') {
result.append("&#47;"); result.pop_back();
result.append("{@*}");
} else { } else {
result.push_back(c); result.push_back(c);
} }
break; break;
case '@': case '@':
// '@' starts phpdoc tags including the @deprecated tag, which will // '@' starts phpdoc tags. Play it safe and escape it.
// cause a compile-time error if inserted before a declaration that result.append("\\");
// does not have a corresponding @Deprecated annotation. result.push_back(c);
result.append("&#64;");
break; break;
default: default:
result.push_back(c); result.push_back(c);
break; break;
} }
prev = c; prev = c;
} }

Loading…
Cancel
Save