Skip to content

Commit

Permalink
Merge 4ca6c6c into 10a42d0
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamCampbell committed Dec 17, 2014
2 parents 10a42d0 + 4ca6c6c commit 1d5fc25
Show file tree
Hide file tree
Showing 7 changed files with 763 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.rst
Expand Up @@ -301,6 +301,13 @@ Choose from the list of available fixers:
phpdoc tags must be aligned
vertically.

* **phpdoc_separation** [symfony] Annotations in phpdocs
should be grouped together so that
annotations of the same type
immediately follow each other, and
annotations of a different type are
separated by a single blank line.

* **remove_leading_slash_use** [symfony] Remove leading slashes in
use clauses.

Expand Down
74 changes: 74 additions & 0 deletions Symfony/CS/DocBlock/Annotation.php
@@ -0,0 +1,74 @@
<?php

/*
* This file is part of the PHP CS utility.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Symfony\CS\DocBlock;

/**
* This represents a special kind of line in a docblock, an annotation.
*
* Although being called a line, it pepresents the entire annotation, including
* its description even it that spans across multiple lines.
*
* @author Graham Campbell <graham@mineuk.com>
*/
class Annotation extends Line
{
/**
* Get the end position of this line.
*
* This method will reliably find the true end of the entire annotation
* including its description.
*
* @return int
*/
public function getEnd()
{
$index = $this->pos;

while ($line = $this->doc->getLine(++$index)) {
if ($line->isAnnotation()) {
return $index - 1;
} elseif (!$line->hasUsefulContent()) {
return $index - 1;
}
}

return $index - 1;
}

/**
* Is this line an annotation?
*
* @return bool
*/
public function isAnnotation()
{
return true;
}

/**
* Get the annotation type.
*
* This may be "param", or "return", etc.
*
* @return string
*/
public function getType()
{
preg_match_all('/@[a-zA-Z]+/', $this->content, $matches);

if (isset($matches[0][0])) {
return strtolower(ltrim($matches[0][0], '@'));
}

return 'other';
}
}
168 changes: 168 additions & 0 deletions Symfony/CS/DocBlock/DocBlock.php
@@ -0,0 +1,168 @@
<?php

/*
* This file is part of the PHP CS utility.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Symfony\CS\DocBlock;

use Symfony\CS\Utils;

/**
* This class represents a docblock.
*
* It internally splits it up into "lines" that we can manipulate.
*
* @author Graham Campbell <graham@mineuk.com>
*/
class DocBlock
{
/**
* The raw array of lines.
*
* @var string[]
*/
private $splits;

/**
* The array of lines.
*
* @var \Symfony\CS\DocBlock\Line[]
*/
private $lines;

/**
* The array of annotations.
*
* @var \Symfony\CS\DocBlock\Annotation[]
*/
private $annotations;

/**
* Create a new docblock instance.
*
* @param string $content
*/
public function __construct($content)
{
$this->splits = Utils::splitLines($content);
}

/**
* Is this content an annotation?
*
* @param string $content
*
* @return bool
*/
private static function isAnnotation($content)
{
return 0 !== preg_match('/\\*\s+@/', $content);
}

/**
* Get this docblock's lines.
*
* @return \Symfony\CS\DocBlock\Line[]
*/
public function getLines()
{
if (null === $this->lines) {
$this->lines = array();
foreach ($this->splits as $pos => $content) {
if (self::isAnnotation($content)) {
$this->lines[$pos] = new Annotation($this, $pos, $content);
} else {
$this->lines[$pos] = new Line($this, $pos, $content);
}
}
}

return $this->lines;
}

/**
* Get a single line.
*
* @param int $pos
*
* @return \Symfony\CS\DocBlock\Line|null
*/
public function getLine($pos)
{
$lines = $this->getLines();

if (isset($lines[$pos])) {
return $lines[$pos];
}
}

/**
* Get this docblock's annotations.
*
* @return \Symfony\CS\DocBlock\Annotation[]
*/
public function getAnnotations()
{
if (null === $this->annotations) {
$this->annotations = array();
foreach ($this->getLines() as $line) {
if ($line->isAnnotation()) {
$this->annotations[] = $line;
}
}
}

return $this->annotations;
}

/**
* Get a single annotation.
*
* @param int $pos
*
* @return \Symfony\CS\DocBlock\Annotation|null
*/
public function getAnnotation($pos)
{
$annotations = $this->getAnnotations();

if (isset($annotations[$pos])) {
return $annotations[$pos];
}
}

/**
* Set the content for a line.
*
* This method should only be called by the line class.
*
* @internal
*
* @param int $pos
* @param string $content
*
* @return void
*/
public function setLineContent($pos, $content)
{
$this->splits[$pos] = $content;
}

/**
* Get the actual content of this docblock.
*
* This method should only be called by the line class.
*
* @return string
*/
public function getContent()
{
return implode($this->splits);
}
}
130 changes: 130 additions & 0 deletions Symfony/CS/DocBlock/Line.php
@@ -0,0 +1,130 @@
<?php

/*
* This file is part of the PHP CS utility.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Symfony\CS\DocBlock;

/**
* This represents a line of a docblock.
*
* @author Graham Campbell <graham@mineuk.com>
*/
class Line
{
/**
* The docblock this line belongs to.
*
* @var \Symfony\CS\DocBlock\Docblock
*/
protected $doc;

/**
* The position of this line in the docblock.
*
* @var \Symfony\CS\DocBlock\Docblock
*/
protected $pos;

/**
* The content of this line.
*
* @var \Symfony\CS\DocBlock\Docblock
*/
protected $content;

/**
* Create a new line instance.
*
* @param \Symfony\CS\DocBlock\Docblock $doc
* @param int $pos
* @param string $content
*/
public function __construct(DocBlock $doc, $pos, $content)
{
$this->doc = $doc;
$this->pos = $pos;
$this->content = $content;
}

/**
* Get the start position of this line.
*
* @return int
*/
public function getStart()
{
return $this->pos;
}

/**
* Get the end position of this line.
*
* @return int
*/
public function getEnd()
{
return $this->pos;
}

/**
* Does this line contain useful content?
*
* This means it is not the the first or final line, and is not empty.
*
* @return bool
*/
public function hasUsefulContent()
{
return 0 !== preg_match('/\\*\s+\S+/', $this->content) && 0 === preg_match('/\\*\//', $this->content);
}

/**
* Is this line an annotation?
*
* @return bool
*/
public function isAnnotation()
{
return false;
}

/**
* Remove this line by clearing its contents.
*
* This method also persist the changes to the docblock class.
*
* @return void
*/
public function remove()
{
$this->content = '';
$this->doc->setLineContent($this->pos, $this->content);
}

/**
* Append a blank docblock line to this line's contents.
*
* This method also persist the changes to the docblock class.
*
* @return void
*/
public function addBlank()
{
preg_match_all('/\ +\*/', $this->content, $matches);

if (isset($matches[0][0])) {
$blank = $matches[0][0];
} else {
$blank = '*';
}

$this->doc->setLineContent($this->pos, $this->content.$blank."\n");
}
}

0 comments on commit 1d5fc25

Please sign in to comment.