PhpdocTrimFixer.php 3.34 KB
Newer Older
Ketan's avatar
Ketan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
<?php

/*
 * 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\Fixer\Phpdoc;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
 * @author Graham Campbell <graham@alt-three.com>
 */
final class PhpdocTrimFixer extends AbstractFixer
{
    /**
     * {@inheritdoc}
     */
    public function getDefinition()
    {
        return new FixerDefinition(
            'PHPDoc should start and end with content, excluding the very first and last line of the docblocks.',
            [new CodeSample('<?php
/**
 *
 * Foo must be final class.
 *
 *
 */
final class Foo {}
')]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        /*
         * Should be run after all phpdoc fixers that add or remove tags, or
         * alter descriptions. This is so that they don't leave behind blank
         * lines this fixer would have otherwise cleaned up.
         */
        return -5;
    }

    /**
     * {@inheritdoc}
     */
    public function isCandidate(Tokens $tokens)
    {
        return $tokens->isTokenKindFound(T_DOC_COMMENT);
    }

    /**
     * {@inheritdoc}
     */
    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
    {
        foreach ($tokens as $index => $token) {
            if (!$token->isGivenKind(T_DOC_COMMENT)) {
                continue;
            }

            $content = $token->getContent();
            $content = $this->fixStart($content);
            // we need re-parse the docblock after fixing the start before
            // fixing the end in order for the lines to be correctly indexed
            $content = $this->fixEnd($content);
            $tokens[$index] = new Token([T_DOC_COMMENT, $content]);
        }
    }

    /**
     * Make sure the first useful line starts immediately after the first line.
     *
     * @param string $content
     *
     * @return string
     */
    private function fixStart($content)
    {
        return Preg::replace(
            '~
                (^/\*\*)                  # DocComment begin
                (?:
                    \R[ \t]*(?:\*[ \t]*)? # lines without useful content
                    (?!\R[ \t]*\*/)       # not followed by a DocComment end
                )+
                (\R[ \t]*(?:\*[ \t]*)?\S) # first line with useful content
            ~x',
            '$1$2',
            $content
        );
    }

    /**
     * Make sure the last useful line is immediately before the final line.
     *
     * @param string $content
     *
     * @return string
     */
    private function fixEnd($content)
    {
        return Preg::replace(
            '~
                (\R[ \t]*(?:\*[ \t]*)?\S.*?) # last line with useful content
                (?:
                    (?<!/\*\*)               # not preceded by a DocComment start
                    \R[ \t]*(?:\*[ \t]*)?    # lines without useful content
                )+
                (\R[ \t]*\*/$)               # DocComment end
            ~xu',
            '$1$2',
            $content
        );
    }
}