123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <?php
- declare(strict_types=1);
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer\DocBlock;
- use PhpCsFixer\Preg;
- use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
- use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
- /**
- * This class represents a docblock.
- *
- * It internally splits it up into "lines" that we can manipulate.
- *
- * @author Graham Campbell <graham@alt-three.com>
- *
- * @final
- */
- final class DocBlock
- {
- /**
- * The array of lines.
- *
- * @var Line[]
- */
- private $lines = [];
- /**
- * The array of annotations.
- *
- * @var null|Annotation[]
- */
- private $annotations;
- /**
- * @var null|NamespaceAnalysis
- */
- private $namespace;
- /**
- * @var NamespaceUseAnalysis[]
- */
- private $namespaceUses;
- public function __construct(string $content, ?NamespaceAnalysis $namespace = null, array $namespaceUses = [])
- {
- foreach (Preg::split('/([^\n\r]+\R*)/', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $line) {
- $this->lines[] = new Line($line);
- }
- $this->namespace = $namespace;
- $this->namespaceUses = $namespaceUses;
- }
- /**
- * Get the string representation of object.
- */
- public function __toString(): string
- {
- return $this->getContent();
- }
- /**
- * Get this docblock's lines.
- *
- * @return Line[]
- */
- public function getLines(): array
- {
- return $this->lines;
- }
- /**
- * Get a single line.
- */
- public function getLine(int $pos): ?Line
- {
- return $this->lines[$pos] ?? null;
- }
- /**
- * Get this docblock's annotations.
- *
- * @return Annotation[]
- */
- public function getAnnotations(): array
- {
- if (null !== $this->annotations) {
- return $this->annotations;
- }
- $this->annotations = [];
- $total = \count($this->lines);
- for ($index = 0; $index < $total; ++$index) {
- if ($this->lines[$index]->containsATag()) {
- // get all the lines that make up the annotation
- $lines = \array_slice($this->lines, $index, $this->findAnnotationLength($index), true);
- $annotation = new Annotation($lines, $this->namespace, $this->namespaceUses);
- // move the index to the end of the annotation to avoid
- // checking it again because we know the lines inside the
- // current annotation cannot be part of another annotation
- $index = $annotation->getEnd();
- // add the current annotation to the list of annotations
- $this->annotations[] = $annotation;
- }
- }
- return $this->annotations;
- }
- public function isMultiLine(): bool
- {
- return 1 !== \count($this->lines);
- }
- /**
- * Take a one line doc block, and turn it into a multi line doc block.
- */
- public function makeMultiLine(string $indent, string $lineEnd): void
- {
- if ($this->isMultiLine()) {
- return;
- }
- $lineContent = $this->getSingleLineDocBlockEntry($this->lines[0]);
- if ('' === $lineContent) {
- $this->lines = [
- new Line('/**'.$lineEnd),
- new Line($indent.' *'.$lineEnd),
- new Line($indent.' */'),
- ];
- return;
- }
- $this->lines = [
- new Line('/**'.$lineEnd),
- new Line($indent.' * '.$lineContent.$lineEnd),
- new Line($indent.' */'),
- ];
- }
- public function makeSingleLine(): void
- {
- if (!$this->isMultiLine()) {
- return;
- }
- $usefulLines = array_filter(
- $this->lines,
- static function (Line $line) {
- return $line->containsUsefulContent();
- }
- );
- if (1 < \count($usefulLines)) {
- return;
- }
- $lineContent = '';
- if (\count($usefulLines)) {
- $lineContent = $this->getSingleLineDocBlockEntry(array_shift($usefulLines));
- }
- $this->lines = [new Line('/** '.$lineContent.' */')];
- }
- public function getAnnotation(int $pos): ?Annotation
- {
- $annotations = $this->getAnnotations();
- return $annotations[$pos] ?? null;
- }
- /**
- * Get specific types of annotations only.
- *
- * If none exist, we're returning an empty array.
- *
- * @param string|string[] $types
- *
- * @return Annotation[]
- */
- public function getAnnotationsOfType($types): array
- {
- $annotations = [];
- $types = (array) $types;
- foreach ($this->getAnnotations() as $annotation) {
- $tag = $annotation->getTag()->getName();
- foreach ($types as $type) {
- if ($type === $tag) {
- $annotations[] = $annotation;
- }
- }
- }
- return $annotations;
- }
- /**
- * Get the actual content of this docblock.
- */
- public function getContent(): string
- {
- return implode('', $this->lines);
- }
- private function findAnnotationLength(int $start): int
- {
- $index = $start;
- while ($line = $this->getLine(++$index)) {
- if ($line->containsATag()) {
- // we've 100% reached the end of the description if we get here
- break;
- }
- if (!$line->containsUsefulContent()) {
- // if next line is also non-useful, or contains a tag, then we're done here
- $next = $this->getLine($index + 1);
- if (null === $next || !$next->containsUsefulContent() || $next->containsATag()) {
- break;
- }
- // otherwise, continue, the annotation must have contained a blank line in its description
- }
- }
- return $index - $start;
- }
- private function getSingleLineDocBlockEntry(Line $line): string
- {
- $lineString = $line->getContent();
- if (0 === \strlen($lineString)) {
- return $lineString;
- }
- $lineString = str_replace('*/', '', $lineString);
- $lineString = trim($lineString);
- if ('/**' === substr($lineString, 0, 3)) {
- $lineString = substr($lineString, 3);
- } elseif ('*' === substr($lineString, 0, 1)) {
- $lineString = substr($lineString, 1);
- }
- return trim($lineString);
- }
- }
|