Chris@18
|
1 <?php
|
Chris@18
|
2 /**
|
Chris@18
|
3 * \Drupal\Sniffs\Commenting\DeprecatedSniff.
|
Chris@18
|
4 *
|
Chris@18
|
5 * @category PHP
|
Chris@18
|
6 * @package PHP_CodeSniffer
|
Chris@18
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@18
|
8 */
|
Chris@18
|
9
|
Chris@18
|
10 namespace Drupal\Sniffs\Commenting;
|
Chris@18
|
11
|
Chris@18
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@18
|
13 use PHP_CodeSniffer\Sniffs\Sniff;
|
Chris@18
|
14
|
Chris@18
|
15 /**
|
Chris@18
|
16 * Ensures standard format of a @deprecated text.
|
Chris@18
|
17 *
|
Chris@18
|
18 * @category PHP
|
Chris@18
|
19 * @package PHP_CodeSniffer
|
Chris@18
|
20 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@18
|
21 */
|
Chris@18
|
22 class DeprecatedSniff implements Sniff
|
Chris@18
|
23 {
|
Chris@18
|
24
|
Chris@18
|
25
|
Chris@18
|
26 /**
|
Chris@18
|
27 * Returns an array of tokens this test wants to listen for.
|
Chris@18
|
28 *
|
Chris@18
|
29 * @return array
|
Chris@18
|
30 */
|
Chris@18
|
31 public function register()
|
Chris@18
|
32 {
|
Chris@18
|
33 return array(T_DOC_COMMENT_TAG);
|
Chris@18
|
34
|
Chris@18
|
35 }//end register()
|
Chris@18
|
36
|
Chris@18
|
37
|
Chris@18
|
38 /**
|
Chris@18
|
39 * Processes this test, when one of its tokens is encountered.
|
Chris@18
|
40 *
|
Chris@18
|
41 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@18
|
42 * @param int $stackPtr The position of the current token
|
Chris@18
|
43 * in the stack passed in $tokens.
|
Chris@18
|
44 *
|
Chris@18
|
45 * @return void
|
Chris@18
|
46 */
|
Chris@18
|
47 public function process(File $phpcsFile, $stackPtr)
|
Chris@18
|
48 {
|
Chris@18
|
49 $tokens = $phpcsFile->getTokens();
|
Chris@18
|
50
|
Chris@18
|
51 // Only process @deprecated tags.
|
Chris@18
|
52 if (strcasecmp($tokens[$stackPtr]['content'], '@deprecated') !== 0) {
|
Chris@18
|
53 return;
|
Chris@18
|
54 }
|
Chris@18
|
55
|
Chris@18
|
56 // Get the end point of the comment block which has the deprecated tag.
|
Chris@18
|
57 $commentEnd = $phpcsFile->findNext(T_DOC_COMMENT_CLOSE_TAG, ($stackPtr + 1));
|
Chris@18
|
58
|
Chris@18
|
59 // Get the full @deprecated text which may cover multiple lines.
|
Chris@18
|
60 $depText = '';
|
Chris@18
|
61 $depEnd = ($stackPtr + 1);
|
Chris@18
|
62 for ($i = ($stackPtr + 1); $i < $commentEnd; $i++) {
|
Chris@18
|
63 if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
|
Chris@18
|
64 if ($tokens[$i]['line'] <= ($tokens[$depEnd]['line'] + 1)) {
|
Chris@18
|
65 $depText .= ' '.$tokens[$i]['content'];
|
Chris@18
|
66 $depEnd = $i;
|
Chris@18
|
67 } else {
|
Chris@18
|
68 break;
|
Chris@18
|
69 }
|
Chris@18
|
70 }
|
Chris@18
|
71
|
Chris@18
|
72 // Found another tag, so we have all the deprecation text.
|
Chris@18
|
73 if ($tokens[$i]['code'] === T_DOC_COMMENT_TAG) {
|
Chris@18
|
74 break;
|
Chris@18
|
75 }
|
Chris@18
|
76 }
|
Chris@18
|
77
|
Chris@18
|
78 $depText = trim($depText);
|
Chris@18
|
79
|
Chris@18
|
80 // The standard format for the deprecation text is:
|
Chris@18
|
81 // @deprecated in %in-version% and will be removed from %removal-version%. %extra-info%.
|
Chris@18
|
82 // Use (?U) 'ungreedy' before the version so that only the text up to
|
Chris@18
|
83 // the first '. ' is matched, as there may be more than one sentence in
|
Chris@18
|
84 // the extra-info part.
|
Chris@18
|
85 $matches = array();
|
Chris@18
|
86 preg_match('/in (.+) and will be removed from (?U)(.+)\. (.+)$/', $depText, $matches);
|
Chris@18
|
87 // There should be 4 items in $matches: 0 is full text, 1 = in-version,
|
Chris@18
|
88 // 2 = removal-version, 3 = extra-info.
|
Chris@18
|
89 if (count($matches) !== 4) {
|
Chris@18
|
90 $error = "The deprecation text '@deprecated %s' does not match the standard format: @deprecated in %%in-version%% and will be removed from %%removal-version%%. %%extra-info%%.";
|
Chris@18
|
91 $phpcsFile->addError($error, $stackPtr, 'IncorrectTextLayout', array($depText));
|
Chris@18
|
92 } else {
|
Chris@18
|
93 // The text follows the basic layout. Now check that the versions
|
Chris@18
|
94 // match drupal:n.n.n or project:n.x-n.n. The text must be all lower
|
Chris@18
|
95 // case and numbers can be one or two digits.
|
Chris@18
|
96 foreach (array('in-version' => $matches[1], 'removal-version' => $matches[2]) as $name => $version) {
|
Chris@18
|
97 if (preg_match('/^drupal:\d{1,2}\.\d{1,2}\.\d{1,2}$/', $version) === 0
|
Chris@18
|
98 && preg_match('/^[a-z\d_]:\d{1,2}\.x\-\d{1,2}\.\d{1,2}$/', $version) === 0
|
Chris@18
|
99 ) {
|
Chris@18
|
100 $error = "The deprecation %s '%s' does not match the standard: drupal:n.n.n or project:n.x-n.n";
|
Chris@18
|
101 $phpcsFile->addWarning($error, $stackPtr, 'DeprecatedVersionFormat', array($name, $version));
|
Chris@18
|
102 }
|
Chris@18
|
103 }
|
Chris@18
|
104 }
|
Chris@18
|
105
|
Chris@18
|
106 // The next tag in this comment block after @deprecated must be @see.
|
Chris@18
|
107 $seeTag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($stackPtr + 1), $commentEnd, false, '@see');
|
Chris@18
|
108 if ($seeTag === false) {
|
Chris@18
|
109 $error = 'Each @deprecated tag must have a @see tag immediately following it.';
|
Chris@18
|
110 $phpcsFile->addError($error, $stackPtr, 'DeprecatedMissingSeeTag');
|
Chris@18
|
111 return;
|
Chris@18
|
112 }
|
Chris@18
|
113
|
Chris@18
|
114 // Check the format of the @see url.
|
Chris@18
|
115 $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, ($seeTag + 1), $commentEnd);
|
Chris@18
|
116 $cr_link = $tokens[$string]['content'];
|
Chris@18
|
117 // Allow for the alternative 'node' or 'project/aaa/issues' format.
|
Chris@18
|
118 preg_match('[^http(s*)://www.drupal.org/(node|project/\w+/issues)/(\d+)(\.*)$]', $cr_link, $matches);
|
Chris@18
|
119 // If matches[4] is not blank it means that the url is correct but it
|
Chris@18
|
120 // ends with a period. As this can be a common mistake give a specific
|
Chris@18
|
121 // message to assist in fixing.
|
Chris@18
|
122 if (isset($matches[4]) === true && empty($matches[4]) === false) {
|
Chris@18
|
123 $error = "The @see url '%s' should not end with a period.";
|
Chris@18
|
124 $phpcsFile->addWarning($error, $seeTag, 'DeprecatedPeriodAfterSeeUrl', array($cr_link));
|
Chris@18
|
125 } else if (empty($matches) === true) {
|
Chris@18
|
126 $error = "The @see url '%s' does not match the standard: http(s)://www.drupal.org/node/n or http(s)://www.drupal.org/project/aaa/issues/n";
|
Chris@18
|
127 $phpcsFile->addWarning($error, $seeTag, 'DeprecatedWrongSeeUrlFormat', array($cr_link));
|
Chris@18
|
128 }
|
Chris@18
|
129
|
Chris@18
|
130 }//end process()
|
Chris@18
|
131
|
Chris@18
|
132
|
Chris@18
|
133 }//end class
|