Skip to content

Commit 2e27757

Browse files
committed
Merge pull request #402 from WordPress-Coding-Standards/develop
0.6.0
2 parents 0460804 + a9a032e commit 2e27757

File tree

10 files changed

+199
-62
lines changed

10 files changed

+199
-62
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ before_script:
3636
- $PHPCS_BIN --config-set installed_paths $(pwd)
3737

3838
script:
39-
- find . \( -name '*.php' \) -exec php -lf {} \;
39+
- if find . -name "*.php" -exec php -l {} \; | grep "^[Parse error|Fatal error]"; then exit 1; fi;
4040
- phpunit --filter WordPress /tmp/phpcs/tests/AllTests.php

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ This projects adheres to [Semantic Versioning](http://semver.org/) and [Keep a C
1111
`get_category_by_slug()`, `get_cat_ID()`, `count_user_posts()`, and `wp_old_slug_redirect()`
1212
to the list of restricted functions in the `WordPress.VIP.RestrictedFunctions` sniff.
1313

14+
## [0.6.0] - 2015-06-30
15+
16+
### Added
17+
- Support for `wp_cache_add()` and `wp_cache_delete()`, as well as custom cache
18+
functions,in the `WordPress.VIP.DirectDatabaseQuery` sniff.
19+
20+
### Removed
21+
- `WordPress.Functions.FunctionRestrictions` and `WordPress.Variables.VariableRestrictions`
22+
from the `WordPress-VIP` standard, since they are just parents for other sniffs.
23+
1424
## [0.5.0] - 2015-06-01
1525

1626
### Added

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,13 @@ The project encompasses a super–set of the sniffs that the WordPress community
9595

9696
You can use the following as standard names when invoking `phpcs` to select sniffs, fitting your needs:
9797

98-
- `WordPress` — all of the sniffs in the project.
99-
- `WordPress-Core` — sniffs that seek to implement the [WordPress core coding standards](http://make.wordpress.org/core/handbook/coding-standards/) and go no further.
100-
- `WordPress-Extra``WordPress-Core` plus extra best practices sniffs, which are not part of core coding standards and could be controversial.
101-
- `WordPress-VIP``WordPress-Core` plus sniffs that seek to implement the [WordPress VIP coding requirements](http://vip.wordpress.com/documentation/code-review-what-we-look-for/).
98+
- `WordPress` — complete set with all of the sniffs in the project
99+
- `WordPress-Core` — main ruleset for [WordPress core coding standards](http://make.wordpress.org/core/handbook/coding-standards/)
100+
- `WordPress-Docs` — additional ruleset for inline documentation
101+
- `WordPress-Extra` — extended ruleset for optional best practices sniffs
102+
- includes `WordPress-Core`
103+
- `WordPress-VIP` — extended ruleset for [WordPress VIP coding requirements](http://vip.wordpress.com/documentation/code-review-what-we-look-for/)
104+
- includes `WordPress-Core`
102105

103106

104107
### Using custom ruleset

WordPress-VIP/ruleset.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
<rule ref="WordPress-Core"/>
66

7-
<!-- the following are inherited by other sniffs -->
8-
<rule ref="WordPress.Functions.FunctionRestrictions"/>
9-
<rule ref="WordPress.Variables.VariableRestrictions"/>
10-
117
<rule ref="WordPress.VIP"/>
128

139
<rule ref="WordPress.XSS.EscapeOutput"/>

WordPress/Sniff.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,40 @@ abstract class WordPress_Sniff implements PHP_CodeSniffer_Sniff {
329329
'wp_die' => true,
330330
);
331331

332+
/**
333+
* A list of functions that get data from the cache.
334+
*
335+
* @since 0.6.0
336+
*
337+
* @var array
338+
*/
339+
public static $cacheGetFunctions = array(
340+
'wp_cache_get' => true,
341+
);
342+
343+
/**
344+
* A list of functions that set data in the cache.
345+
*
346+
* @since 0.6.0
347+
*
348+
* @var array
349+
*/
350+
public static $cacheSetFunctions = array(
351+
'wp_cache_set' => true,
352+
'wp_cache_add' => true,
353+
);
354+
355+
/**
356+
* A list of functions that delete data from the cache.
357+
*
358+
* @since 0.6.0
359+
*
360+
* @var array
361+
*/
362+
public static $cacheDeleteFunctions = array(
363+
'wp_cache_delete' => true,
364+
);
365+
332366
/**
333367
* The current file being sniffed.
334368
*

WordPress/Sniffs/Functions/FunctionRestrictionsSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr )
9393
$groups = $this->getGroups();
9494

9595
if ( empty( $groups ) ) {
96-
return;
96+
return count( $tokens ) + 1;
9797
}
9898

9999
foreach ( $groups as $groupName => $group ) {

WordPress/Sniffs/VIP/DirectDatabaseQuerySniff.php

Lines changed: 100 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,56 @@
1212
class WordPress_Sniffs_VIP_DirectDatabaseQuerySniff implements PHP_CodeSniffer_Sniff
1313
{
1414

15+
/**
16+
* List of custom cache get functions.
17+
*
18+
* @since 0.6.0
19+
*
20+
* @var string[]
21+
*/
22+
public $customCacheGetFunctions = array();
23+
24+
/**
25+
* List of custom cache set functions.
26+
*
27+
* @since 0.6.0
28+
*
29+
* @var string[]
30+
*/
31+
public $customCacheSetFunctions = array();
32+
33+
/**
34+
* List of custom cache delete functions.
35+
*
36+
* @since 0.6.0
37+
*
38+
* @var string[]
39+
*/
40+
public $customCacheDeleteFunctions = array();
41+
42+
/**
43+
* The lists of $wpdb methods.
44+
*
45+
* @since 0.6.0
46+
*
47+
* @var array[]
48+
*/
49+
protected static $methods = array(
50+
'cachable' => array(
51+
'delete' => true,
52+
'get_var' => true,
53+
'get_col' => true,
54+
'get_row' => true,
55+
'get_results' => true,
56+
'query' => true,
57+
'replace' => true,
58+
'update' => true,
59+
),
60+
'noncachable' => array(
61+
'insert' => true,
62+
),
63+
);
64+
1565
/**
1666
* Returns an array of tokens this test wants to listen for.
1767
*
@@ -33,10 +83,29 @@ public function register()
3383
* @param int $stackPtr The position of the current token
3484
* in the stack passed in $tokens.
3585
*
36-
* @return void
86+
* @return int|void
3787
*/
3888
public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr )
3989
{
90+
if ( ! isset( self::$methods['all'] ) ) {
91+
self::$methods['all'] = array_merge( self::$methods['cachable'], self::$methods['noncachable'] );
92+
93+
WordPress_Sniff::$cacheGetFunctions = array_merge(
94+
WordPress_Sniff::$cacheGetFunctions,
95+
array_flip( $this->customCacheGetFunctions )
96+
);
97+
98+
WordPress_Sniff::$cacheSetFunctions = array_merge(
99+
WordPress_Sniff::$cacheSetFunctions,
100+
array_flip( $this->customCacheSetFunctions )
101+
);
102+
103+
WordPress_Sniff::$cacheDeleteFunctions = array_merge(
104+
WordPress_Sniff::$cacheDeleteFunctions,
105+
array_flip( $this->customCacheDeleteFunctions )
106+
);
107+
}
108+
40109
$tokens = $phpcsFile->getTokens();
41110

42111
// Check for $wpdb variable
@@ -50,11 +119,7 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr )
50119
$methodPtr = $phpcsFile->findNext( array( T_WHITESPACE ), $is_object_call + 1, null, true, null, true );
51120
$method = $tokens[ $methodPtr ]['content'];
52121

53-
$methods = array(
54-
'cachable' => array( 'get_var', 'get_col', 'get_row', 'get_results', 'query' ),
55-
'noncachable' => array( 'execute', 'insert', 'update', 'replace' ),
56-
);
57-
if ( ! in_array( $method, array_merge( $methods['cachable'], $methods['noncachable'] ) ) ) {
122+
if ( ! isset( self::$methods['all'][ $method ] ) ) {
58123
return;
59124
}
60125

@@ -81,79 +146,64 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr )
81146
$_pos = $stackPtr;
82147
while ( $_pos = $phpcsFile->findNext( array( T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING ), $_pos + 1, $endOfStatement, null, null, true ) ) {
83148
if ( preg_match( '#\b(ALTER|CREATE|DROP)\b#i', $tokens[$_pos]['content'], $matches ) > 0 ) {
84-
$message = 'Attempting a database schema change is highly discouraged.';
85-
$this->add_unique_message( $phpcsFile, 'error', $_pos, $tokens[$_pos]['line'], $message );
149+
$phpcsFile->addError( 'Attempting a database schema change is highly discouraged.', $_pos, 'SchemaChange' );
86150
}
87151
}
88152

89153
// Flag instance if not whitelisted
90154
if ( ! $whitelisted_db_call ) {
91-
$message = 'Usage of a direct database call is discouraged.';
92-
$this->add_unique_message( $phpcsFile, 'warning', $stackPtr, $tokens[$stackPtr]['line'], $message );
155+
$phpcsFile->addWarning( 'Usage of a direct database call is discouraged.', $stackPtr, 'DirectQuery' );
93156
}
94157

95-
if ( ! in_array( $method, $methods['cachable'] ) ) {
96-
return;
158+
if ( ! isset( self::$methods['cachable'][ $method ] ) ) {
159+
return $endOfStatement;
97160
}
98161

99162
$whitelisted_cache = false;
100-
$cached = false;
163+
$cached = $wp_cache_get = false;
101164
if ( preg_match( '/cache\s+(ok|pass|clear|whitelist)/i', $endOfLineComment, $matches ) ) {
102165
$whitelisted_cache = true;
103166
}
104167
if ( ! $whitelisted_cache && ! empty( $tokens[$stackPtr]['conditions'] ) ) {
105-
$conditions = $tokens[$stackPtr]['conditions'];
106-
$scope_function = null;
107-
foreach ( $conditions as $condPtr => $condType ) {
108-
if ( $condType == T_FUNCTION ) {
109-
$scope_function = $condPtr;
110-
}
111-
}
168+
$scope_function = $phpcsFile->getCondition( $stackPtr, T_FUNCTION );
112169

113170
if ( $scope_function ) {
114171
$scopeStart = $tokens[$scope_function]['scope_opener'];
115172
$scopeEnd = $tokens[$scope_function]['scope_closer'];
116173

117-
$wpcacheget = $phpcsFile->findNext( array( T_STRING ), $scopeStart + 1, $stackPtr - 1, null, 'wp_cache_get' );
118-
$wpcacheset = $phpcsFile->findNext( array( T_STRING ), $stackPtr + 1, $scopeEnd - 1, null, 'wp_cache_set' );
174+
for ( $i = $scopeStart + 1; $i < $scopeEnd; $i++ ) {
175+
if ( T_STRING === $tokens[ $i ]['code'] ) {
119176

120-
if ( $wpcacheget && $wpcacheset ) {
121-
$cached = true;
122-
}
123-
}
177+
if ( isset( WordPress_Sniff::$cacheDeleteFunctions[ $tokens[ $i ]['content'] ] ) ) {
124178

125-
}
179+
if ( in_array( $method, array( 'query', 'update', 'replace', 'delete' ) ) ) {
180+
$cached = true;
181+
break;
182+
}
126183

127-
if ( ! $cached && ! $whitelisted_cache ) {
128-
$message = 'Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.';
129-
$this->add_unique_message( $phpcsFile, 'error', $stackPtr, $tokens[$stackPtr]['line'], $message );
130-
}
184+
} elseif ( isset( WordPress_Sniff::$cacheGetFunctions[ $tokens[ $i ]['content'] ] ) ) {
131185

132-
}//end process()
186+
$wp_cache_get = true;
133187

134-
/**
135-
* Add unique message per line
136-
* @param PHP_CodeSniffer_File $phpcsFile
137-
* @param string $type (error|warning)
138-
* @param int $pointer
139-
* @param int $line
140-
* @param string $message
141-
* @return void
142-
*/
143-
function add_unique_message( PHP_CodeSniffer_File $phpcsFile, $type, $pointer, $line, $message ) {
144-
$messages = call_user_func( array( $phpcsFile, 'get' . ucfirst( $type . 's' ) ) );
145-
if ( isset( $messages[$line] ) ) {
146-
foreach ( $messages[$line] as $idx => $events ) {
147-
foreach ( $events as $arr ) {
148-
if ( $arr['message'] == $message ) {
149-
return false;
188+
} elseif ( isset( WordPress_Sniff::$cacheSetFunctions[ $tokens[ $i ]['content'] ] ) ) {
189+
190+
if ( $wp_cache_get ) {
191+
$cached = true;
192+
break;
193+
}
194+
}
150195
}
151196
}
152197
}
153198
}
154199

155-
call_user_func( array( $phpcsFile, 'add' . ucfirst( $type ) ), $message, $pointer );
156-
}
200+
if ( ! $cached && ! $whitelisted_cache ) {
201+
$message = 'Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set or wp_cache_delete.';
202+
$phpcsFile->addError( $message, $stackPtr, 'NoCaching' );
203+
}
157204

205+
return $endOfStatement;
206+
207+
}//end process()
158208

159209
}//end class

WordPress/Sniffs/Variables/VariableRestrictionsSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr )
8686
$groups = $this->getGroups();
8787

8888
if ( empty( $groups ) ) {
89-
return;
89+
return count( $tokens ) + 1;
9090
}
9191

9292
// Check if it is a function not a variable

WordPress/Tests/VIP/DirectDatabaseQueryUnitTest.inc

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,45 @@ function taz() {
6161
/* @var wpdb $wpdb */
6262
global $wpdb;
6363
echo $wpdb->insert_id; // Good, no actual call, and doesn't need any caching
64-
}
64+
}
65+
66+
// Some $wpdb methods can pass with only deleting the cache.
67+
function cache_delete_only() {
68+
global $wpdb;
69+
70+
$data = $where = array();
71+
72+
// These methods are allowed to be used with just wp_cache_delete().
73+
$wpdb->update( $wpdb->users, $data, $where ); // db call ok; OK
74+
$wpdb->replace( $wpdb->users, $data, $where ); // db call ok; OK
75+
$wpdb->delete( $wpdb->users, $data, $where ); // db call ok; OK
76+
$wpdb->query( 'SELECT X FROM Y' ); // db call ok; OK
77+
78+
$wpdb->get_results( 'SELECT X FROM Y' ); // db call ok; Bad
79+
$wpdb->get_row( 'SELECT X FROM Y' ); // db call ok; Bad
80+
$wpdb->get_col( 'SELECT X FROM Y' ); // db call ok; Bad
81+
82+
wp_cache_delete( 'key', 'group' );
83+
}
84+
85+
// It is OK to use the wp_cache_add() function in place of wp_cache_set().
86+
function cache_add_instead_of_set() {
87+
global $wpdb;
88+
89+
$baz = wp_cache_get( 'baz' );
90+
91+
if ( false !== $baz ) {
92+
93+
$data = $where = array();
94+
95+
$wpdb->update( $wpdb->users, $data, $where ); // db call ok; OK
96+
$wpdb->replace( $wpdb->users, $data, $where ); // db call ok; OK
97+
$wpdb->delete( $wpdb->users, $data, $where ); // db call ok; OK
98+
$wpdb->query( 'SELECT X FROM Y' ); // db call ok; OK
99+
$wpdb->get_row( 'SELECT X FROM Y' ); // db call ok; OK
100+
$wpdb->get_col( 'SELECT X FROM Y' ); // db call ok; OK
101+
$baz = $wpdb->get_results( $wpdb->prepare( 'SELECT X FROM Y ' ) ); // db call ok; OK
102+
103+
wp_cache_add( 'baz', $baz );
104+
}
105+
}

WordPress/Tests/VIP/DirectDatabaseQueryUnitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public function getErrorList()
3030
32 => 1,
3131
34 => 1,
3232
50 => 1,
33+
78 => 1,
34+
79 => 1,
35+
80 => 1,
3336
);
3437

3538
}//end getErrorList()

0 commit comments

Comments
 (0)