Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
DocBlocks
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
4 / 4
18
100.00% covered (success)
100.00%
1 / 1
 findDocBlockForDeclaration
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 findDocBlockInsertionPointForDeclaration
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getCommentTags
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
7
 getIndent
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2/**
3 * Utility class with functions for processing doc blocks.
4 *
5 * @package automattic/jetpack-codesniffer
6 */
7
8namespace Automattic\Jetpack\Codesniffer\Utils;
9
10use PHP_CodeSniffer\Exceptions\RuntimeException;
11use PHP_CodeSniffer\Files\File;
12use PHP_CodeSniffer\Util\Tokens as PHPCS_Tokens;
13
14/**
15 * Utility class with functions for processing doc blocks.
16 */
17class DocBlocks {
18
19    /**
20     * Find the doc block for a class, interface, trait, function, property, constant, or method declaration.
21     *
22     * @param File $phpcsFile The file where the token was found.
23     * @param int  $stackPtr Token position. Must be a T_CLASS, T_INTERFACE, T_TRAIT, T_FUNCTION, T_VARIABLE, or T_CONST.
24     * @return int|false Position of the T_DOC_COMMENT_OPEN_TAG for the class, if any is found.
25     * @throws RuntimeException If $stackPtr is not a supported token.
26     */
27    public static function findDocBlockForDeclaration( File $phpcsFile, $stackPtr ) {
28        $valid = Tokens::tokensPreceedingDeclaration( $phpcsFile, $stackPtr );
29        return Navigation::findPreviousInRun( $phpcsFile, T_DOC_COMMENT_OPEN_TAG, $valid, $stackPtr - 1 );
30    }
31
32    /**
33     * Find the token before which to insert a doc block for a declaration.
34     *
35     * Assumes no doc block already exists for the declaration.
36     * If one does exist, the returned insertion point may be before or after it.
37     *
38     * @param File $phpcsFile The file where the token was found.
39     * @param int  $stackPtr Token position. Must be a T_CLASS, T_INTERFACE, T_TRAIT, T_FUNCTION, T_VARIABLE, or T_CONST.
40     * @return int Position to insert a doc block before.
41     * @throws RuntimeException If $stackPtr is not a supported token.
42     */
43    public static function findDocBlockInsertionPointForDeclaration( File $phpcsFile, $stackPtr ) {
44        $valid = Tokens::tokensPreceedingDeclaration( $phpcsFile, $stackPtr );
45
46        // Find the start of the declaration.
47        $idx = Navigation::findStartOfRun( $phpcsFile, $valid, $stackPtr - 1 );
48        if ( $idx === false ) {
49            // Nothing relevant before it, so insert before the declaration token itself.
50            return $stackPtr;
51        }
52
53        // Move forward past any leading whitespace or comments. Probably not really part of the declaration.
54        $idx2 = $phpcsFile->findNext( PHPCS_Tokens::$emptyTokens, $idx, $stackPtr + 1, true );
55        return $idx2 === false ? $idx : $idx2;
56    }
57
58    /**
59     * Get tags in a doc comment.
60     *
61     * @param File $phpcsFile The file where the token was found.
62     * @param int  $stackPtr T_DOC_COMMENT_OPEN_TAG token position.
63     * @return array{name:string,content:string,ptr:int,startptr:int,endptr:int}[] Array of tags from the comment.
64     *   - name: (string) Tag name, e.g. `@param`.
65     *   - content: (string) Content of the tag, ignoring any T_DOC_COMMENT_STAR tokens. Whitespace is trimmed.
66     *   - ptr: (int) Location of the T_DOC_COMMENT_TAG token for the tag.
67     *   - startptr: (int) Location of the first token for the tag.
68     *   - endptr: (int) Location of the last token for the tag.
69     * @throws RuntimeException If $stackPtr is not a T_DOC_COMMENT_OPEN_TAG.
70     */
71    public static function getCommentTags( File $phpcsFile, $stackPtr ) {
72        $tokens = $phpcsFile->getTokens();
73        if ( $tokens[ $stackPtr ]['code'] !== T_DOC_COMMENT_OPEN_TAG ) {
74            throw new RuntimeException( '$stackPtr must be of type T_DOC_COMMENT_OPEN_TAG' );
75        }
76
77        $ret = array();
78        foreach ( $tokens[ $stackPtr ]['comment_tags'] as $i => $idx ) {
79            $name    = $tokens[ $idx ]['content'];
80            $end     = $tokens[ $stackPtr ]['comment_tags'][ $i + 1 ] ?? $tokens[ $stackPtr ]['comment_closer'];
81            $content = '';
82            for ( $j = $idx + 1; $j < $end; $j++ ) {
83                if ( $tokens[ $j ]['code'] !== T_DOC_COMMENT_STAR ) {
84                    $content .= trim( $tokens[ $j ]['content'], " \t" );
85                }
86            }
87
88            $idx2  = Navigation::findPreviousInRun( $phpcsFile, T_DOC_COMMENT_WHITESPACE, array( T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR ), $idx - 1, null, "\n" );
89            $start = $idx2 === false ? $idx : $idx2 + 1;
90
91            $idx2 = Navigation::findPreviousInRun( $phpcsFile, T_DOC_COMMENT_WHITESPACE, array( T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR ), $end - 1, null, "\n" );
92            $end  = $idx2 === false ? $end - 1 : $idx2;
93
94            $ret[] = array(
95                'name'     => $name,
96                'content'  => trim( $content ),
97                'ptr'      => $idx,
98                'startptr' => $start,
99                'endptr'   => $end,
100            );
101        }
102
103        return $ret;
104    }
105
106    /**
107     * Get a suggested indent for adding to the doc comment.
108     *
109     * Suggested indent includes the one space before the `*` leading a line.
110     *
111     * @param File $phpcsFile The file where the token was found.
112     * @param int  $stackPtr T_DOC_COMMENT_OPEN_TAG token position.
113     * @return string Suggested indentation.
114     * @throws RuntimeException If $stackPtr is not a T_DOC_COMMENT_OPEN_TAG.
115     */
116    public static function getIndent( File $phpcsFile, $stackPtr ) {
117        $tokens = $phpcsFile->getTokens();
118        if ( $tokens[ $stackPtr ]['code'] !== T_DOC_COMMENT_OPEN_TAG ) {
119            throw new RuntimeException( '$stackPtr must be of type T_DOC_COMMENT_OPEN_TAG' );
120        }
121
122        // If we can, read the indent of the first T_DOC_COMMENT_STAR. Otherwise try reading the indent of the T_DOC_COMMENT_OPEN_TAG itself and add a space.
123        $idx   = $phpcsFile->findNext( T_DOC_COMMENT_STAR, $stackPtr, $tokens[ $stackPtr ]['comment_closer'] );
124        $extra = '';
125        if ( $idx === false ) {
126            $idx   = $stackPtr;
127            $extra = ' ';
128        }
129
130        $line = $tokens[ $idx ]['line'];
131        $ret  = '';
132        while ( --$idx >= 0 && $tokens[ $idx ]['line'] === $line ) {
133            if ( $tokens[ $idx ]['code'] === T_DOC_COMMENT_WHITESPACE || $tokens[ $idx ]['code'] === T_WHITESPACE ) {
134                $ret = ( $tokens[ $idx ]['orig_content'] ?? $tokens[ $idx ]['content'] ) . $ret;
135            } else {
136                $ret = str_repeat( ' ', strlen( $tokens[ $idx ]['content'] ) ) . $ret;
137            }
138        }
139        return $ret . $extra;
140    }
141}