diff --git a/src/DocBlock/Serializer.php b/src/DocBlock/Serializer.php index 84da3f7..7b3dd55 100644 --- a/src/DocBlock/Serializer.php +++ b/src/DocBlock/Serializer.php @@ -13,13 +13,10 @@ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock; +use Webmozart\Assert\Assert; /** - * Serializes a DocBlock instance. - * - * @author Barry vd. Heuvel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org + * Converts a DocBlock back from an object to a complete DocComment including Asterisks. */ class Serializer { @@ -29,7 +26,7 @@ class Serializer /** @var int The number of times the indent string is repeated. */ protected $indent = 0; - /** @var bool Whether to indent the first line. */ + /** @var bool Whether to indent the first line with the given indent amount and string. */ protected $isFirstLineIndented = true; /** @var int|null The max length of a line. */ @@ -38,130 +35,28 @@ class Serializer /** * Create a Serializer instance. * - * @param int $indent The number of times the indent string is - * repeated. + * @param int $indent The number of times the indent string is repeated. * @param string $indentString The string to indent the comment with. * @param bool $indentFirstLine Whether to indent the first line. - * @param int|null $lineLength The max length of a line or NULL to - * disable line wrapping. + * @param int|null $lineLength The max length of a line or NULL to disable line wrapping. */ - public function __construct( - $indent = 0, - $indentString = ' ', - $indentFirstLine = true, - $lineLength = null - ) { - $this->setIndentationString($indentString); - $this->setIndent($indent); - $this->setIsFirstLineIndented($indentFirstLine); - $this->setLineLength($lineLength); - } - - /** - * Sets the string to indent comments with. - * - * @param string $indentString The string to indent comments with. - * - * @return $this This serializer object. - */ - public function setIndentationString($indentString) + public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null) { - $this->indentString = (string)$indentString; + Assert::integer($indent); + Assert::string($indentString); + Assert::boolean($indentFirstLine); + Assert::nullOrInteger($lineLength); - return $this; - } - - /** - * Gets the string to indent comments with. - * - * @return string The indent string. - */ - public function getIndentationString() - { - return $this->indentString; - } - - /** - * Sets the number of indents. - * - * @param int $indent The number of times the indent string is repeated. - * - * @return $this This serializer object. - */ - public function setIndent($indent) - { - $this->indent = (int)$indent; - return $this; - } - - /** - * Gets the number of indents. - * - * @return int The number of times the indent string is repeated. - */ - public function getIndent() - { - return $this->indent; - } - - /** - * Sets whether or not the first line should be indented. - * - * Sets whether or not the first line (the one with the "/**") should be - * indented. - * - * @param bool $indentFirstLine The new value for this setting. - * - * @return $this This serializer object. - */ - public function setIsFirstLineIndented($indentFirstLine) - { - $this->isFirstLineIndented = (bool)$indentFirstLine; - return $this; - } - - /** - * Gets whether or not the first line should be indented. - * - * @return bool Whether or not the first line should be indented. - */ - public function isFirstLineIndented() - { - return $this->isFirstLineIndented; - } - - /** - * Sets the line length. - * - * Sets the length of each line in the serialization. Content will be - * wrapped within this limit. - * - * @param int|null $lineLength The length of each line. NULL to disable line - * wrapping altogether. - * - * @return $this This serializer object. - */ - public function setLineLength($lineLength) - { - $this->lineLength = null === $lineLength ? null : (int)$lineLength; - return $this; - } - - /** - * Gets the line length. - * - * @return int|null The length of each line or NULL if line wrapping is - * disabled. - */ - public function getLineLength() - { - return $this->lineLength; + $this->indent = $indent; + $this->indentString = $indentString; + $this->isFirstLineIndented = $indentFirstLine; + $this->lineLength = $lineLength; } /** * Generate a DocBlock comment. * - * @param DocBlock The DocBlock to serialize. + * @param DocBlock $docblock The DocBlock to serialize. * * @return string The serialized doc block. */ @@ -169,22 +64,71 @@ class Serializer { $indent = str_repeat($this->indentString, $this->indent); $firstIndent = $this->isFirstLineIndented ? $indent : ''; + // 3 === strlen(' * ') + $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null; - $wrapLength = 80; - $text = $docblock->getText(); - if ($this->lineLength) { - //3 === strlen(' * ') - $wrapLength = $this->lineLength - strlen($indent) - 3; - $text = wordwrap($text, $wrapLength); - } - $text = str_replace("\n", "\n{$indent} * ", $text); + $text = $this->removeTrailingSpaces( + $indent, + $this->addAsterisksForEachLine( + $indent, + $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength) + ) + ); $comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n"; + $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); + $comment .= $indent . ' */'; - /** @var Tag $tag */ + return $comment; + } + + /** + * @param $indent + * @param $text + * @return mixed + */ + private function removeTrailingSpaces($indent, $text) + { + return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text); + } + + /** + * @param $indent + * @param $text + * @return mixed + */ + private function addAsterisksForEachLine($indent, $text) + { + return str_replace("\n", "\n{$indent} * ", $text); + } + + /** + * @param DocBlock $docblock + * @param $wrapLength + * @return string + */ + private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) + { + $text = $docblock->getSummary() . "\n\n" . $docblock->getDescription(); + if ($wrapLength !== null) { + $text = wordwrap($text, $wrapLength); + return $text; + } + return $text; + } + + /** + * @param DocBlock $docblock + * @param $wrapLength + * @param $indent + * @param $comment + * @return string + */ + private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment) + { foreach ($docblock->getTags() as $tag) { - $tagText = (string) $tag; - if ($this->lineLength) { + $tagText = (string)$tag; + if ($wrapLength !== null) { $tagText = wordwrap($tagText, $wrapLength); } $tagText = str_replace("\n", "\n{$indent} * ", $tagText); @@ -192,8 +136,6 @@ class Serializer $comment .= "{$indent} * {$tagText}\n"; } - $comment .= $indent . ' */'; - return $comment; } } diff --git a/tests/unit/DocBlock/SerializerTest.php b/tests/unit/DocBlock/SerializerTest.php new file mode 100644 index 0000000..2132d5c --- /dev/null +++ b/tests/unit/DocBlock/SerializerTest.php @@ -0,0 +1,201 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use Mockery as m; +use phpDocumentor\Reflection\DocBlock; + +/** + * @coversDefaultClass \phpDocumentor\Reflection\DocBlock\Serializer + * @covers :: + */ +class SerializerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers ::__construct + * @covers ::getDocComment + * @uses phpDocumentor\Reflection\DocBlock\Description + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + * @uses phpDocumentor\Reflection\DocBlock + * @uses phpDocumentor\Reflection\DocBlock\Tags\BaseTag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Other + */ + public function testReconstructsADocCommentFromADocBlock() + { + $expected = <<<'DOCCOMMENT' +/** + * This is a summary + * + * This is a description + * + * @unknown-tag Test description for the unknown tag + */ +DOCCOMMENT; + + $fixture = new Serializer(); + + $docBlock = new DocBlock( + 'This is a summary', + new Description('This is a description'), + [ + new DocBlock\Tags\Other('unknown-tag', new Description('Test description for the unknown tag')) + ] + ); + + $this->assertSame($expected, $fixture->getDocComment($docBlock)); + } + + /** + * @covers ::__construct + * @covers ::getDocComment + * @uses phpDocumentor\Reflection\DocBlock\Description + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + * @uses phpDocumentor\Reflection\DocBlock + * @uses phpDocumentor\Reflection\DocBlock\Tags\BaseTag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Other + */ + public function testAddPrefixToDocBlock() + { + $expected = <<<'DOCCOMMENT' +aa/** +aa * This is a summary +aa * +aa * This is a description +aa * +aa * @unknown-tag Test description for the unknown tag +aa */ +DOCCOMMENT; + + $fixture = new Serializer(2, 'a'); + + $docBlock = new DocBlock( + 'This is a summary', + new Description('This is a description'), + [ + new DocBlock\Tags\Other('unknown-tag', new Description('Test description for the unknown tag')) + ] + ); + + $this->assertSame($expected, $fixture->getDocComment($docBlock)); + } + + /** + * @covers ::__construct + * @covers ::getDocComment + * @uses phpDocumentor\Reflection\DocBlock\Description + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + * @uses phpDocumentor\Reflection\DocBlock + * @uses phpDocumentor\Reflection\DocBlock\Tags\BaseTag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Other + */ + public function testAddPrefixToDocBlockExceptFirstLine() + { + $expected = <<<'DOCCOMMENT' +/** +aa * This is a summary +aa * +aa * This is a description +aa * +aa * @unknown-tag Test description for the unknown tag +aa */ +DOCCOMMENT; + + $fixture = new Serializer(2, 'a', false); + + $docBlock = new DocBlock( + 'This is a summary', + new Description('This is a description'), + [ + new DocBlock\Tags\Other('unknown-tag', new Description('Test description for the unknown tag')) + ] + ); + + $this->assertSame($expected, $fixture->getDocComment($docBlock)); + } + + /** + * @covers ::__construct + * @covers ::getDocComment + * @uses phpDocumentor\Reflection\DocBlock\Description + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + * @uses phpDocumentor\Reflection\DocBlock + * @uses phpDocumentor\Reflection\DocBlock\Tags\BaseTag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Other + */ + public function testWordwrapsAroundTheGivenAmountOfCharacters() + { + $expected = <<<'DOCCOMMENT' +/** + * This is a + * summary + * + * This is a + * description + * + * @unknown-tag + * Test + * description + * for the + * unknown tag + */ +DOCCOMMENT; + + $fixture = new Serializer(0, '', true, 15); + + $docBlock = new DocBlock( + 'This is a summary', + new Description('This is a description'), + [ + new DocBlock\Tags\Other('unknown-tag', new Description('Test description for the unknown tag')) + ] + ); + + $this->assertSame($expected, $fixture->getDocComment($docBlock)); + } + + /** + * @covers ::__construct + * @expectedException \InvalidArgumentException + */ + public function testInitializationFailsIfIndentIsNotAnInteger() + { + new Serializer([]); + } + + /** + * @covers ::__construct + * @expectedException \InvalidArgumentException + */ + public function testInitializationFailsIfIndentStringIsNotAString() + { + new Serializer(0, []); + } + + /** + * @covers ::__construct + * @expectedException \InvalidArgumentException + */ + public function testInitializationFailsIfIndentFirstLineIsNotABoolean() + { + new Serializer(0, '', []); + } + + /** + * @covers ::__construct + * @expectedException \InvalidArgumentException + */ + public function testInitializationFailsIfLineLengthIsNotNullNorAnInteger() + { + new Serializer(0, '', false, []); + } +}