Skip to content

Commit 5f3905a

Browse files
committed
Sniff::is_validated(): allow for array_key_exists()
Just like, `isset()` and `empty()`, `array_key_exists()` is a way to validate a variable exists and should therefore be recognized by this function.
1 parent 4e8d27f commit 5f3905a

File tree

1 file changed

+78
-19
lines changed

1 file changed

+78
-19
lines changed

WordPress/Sniff.php

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,7 +1703,7 @@ protected function get_array_access_key( $stackPtr ) {
17031703
}
17041704

17051705
/**
1706-
* Check if the existence of a variable is validated with isset() or empty().
1706+
* Check if the existence of a variable is validated with isset(), empty() or array_key_exists().
17071707
*
17081708
* When $in_condition_only is false, (which is the default), this is considered
17091709
* valid:
@@ -1726,6 +1726,7 @@ protected function get_array_access_key( $stackPtr ) {
17261726
* ```
17271727
*
17281728
* @since 0.5.0
1729+
* @since 2.0.1 Now recognizes array_key_exists() as a validation function.
17291730
*
17301731
* @param int $stackPtr The index of this token in the stack.
17311732
* @param string $array_key An array key to check for ("bar" in $foo['bar']).
@@ -1791,36 +1792,94 @@ protected function is_validated( $stackPtr, $array_key = null, $in_condition_onl
17911792
}
17921793

17931794
$bare_array_key = $this->strip_quotes( $array_key );
1795+
$targets = array(
1796+
\T_ISSET => 'construct',
1797+
\T_EMPTY => 'construct',
1798+
\T_UNSET => 'construct',
1799+
\T_STRING => 'function_call',
1800+
);
17941801

17951802
// phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer.Found -- On purpose, see below.
17961803
for ( $i = ( $scope_start + 1 ); $i < $scope_end; $i++ ) {
17971804

1798-
if ( ! \in_array( $this->tokens[ $i ]['code'], array( \T_ISSET, \T_EMPTY, \T_UNSET ), true ) ) {
1805+
if ( isset( $targets[ $this->tokens[ $i ]['code'] ] ) === false ) {
17991806
continue;
18001807
}
18011808

1802-
$issetOpener = $this->phpcsFile->findNext( \T_OPEN_PARENTHESIS, $i );
1803-
$issetCloser = $this->tokens[ $issetOpener ]['parenthesis_closer'];
1809+
switch ( $targets[ $this->tokens[ $i ]['code'] ] ) {
1810+
case 'construct':
1811+
$issetOpener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true, null, true );
1812+
if ( false === $issetOpener || \T_OPEN_PARENTHESIS !== $this->tokens[ $issetOpener ]['code'] ) {
1813+
// Parse error or live coding.
1814+
continue 2;
1815+
}
18041816

1805-
// Look for this variable. We purposely stomp $i from the parent loop.
1806-
for ( $i = ( $issetOpener + 1 ); $i < $issetCloser; $i++ ) {
1817+
$issetCloser = $this->tokens[ $issetOpener ]['parenthesis_closer'];
18071818

1808-
if ( \T_VARIABLE !== $this->tokens[ $i ]['code'] ) {
1809-
continue;
1810-
}
1819+
// Look for this variable. We purposely stomp $i from the parent loop.
1820+
for ( $i = ( $issetOpener + 1 ); $i < $issetCloser; $i++ ) {
18111821

1812-
if ( $this->tokens[ $stackPtr ]['content'] !== $this->tokens[ $i ]['content'] ) {
1813-
continue;
1814-
}
1822+
if ( \T_VARIABLE !== $this->tokens[ $i ]['code'] ) {
1823+
continue;
1824+
}
18151825

1816-
// If we're checking for a specific array key (ex: 'hello' in
1817-
// $_POST['hello']), that must match too. Quote-style, however, doesn't matter.
1818-
if ( isset( $array_key )
1819-
&& $this->strip_quotes( $this->get_array_access_key( $i ) ) !== $bare_array_key ) {
1820-
continue;
1821-
}
1826+
if ( $this->tokens[ $stackPtr ]['content'] !== $this->tokens[ $i ]['content'] ) {
1827+
continue;
1828+
}
18221829

1823-
return true;
1830+
// If we're checking for a specific array key (ex: 'hello' in
1831+
// $_POST['hello']), that must match too. Quote-style, however, doesn't matter.
1832+
if ( isset( $array_key )
1833+
&& $this->strip_quotes( $this->get_array_access_key( $i ) ) !== $bare_array_key ) {
1834+
continue;
1835+
}
1836+
1837+
return true;
1838+
}
1839+
1840+
break;
1841+
1842+
case 'function_call':
1843+
// Only check calls to array_key_exists().
1844+
if ( 'array_key_exists' !== $this->tokens[ $i ]['content'] ) {
1845+
continue 2;
1846+
}
1847+
1848+
$next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true, null, true );
1849+
if ( false === $next_non_empty || \T_OPEN_PARENTHESIS !== $this->tokens[ $next_non_empty ]['code'] ) {
1850+
// Not a function call.
1851+
continue 2;
1852+
}
1853+
1854+
$previous_non_empty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $i - 1 ), null, true, null, true );
1855+
if ( false !== $previous_non_empty ) {
1856+
if ( \T_OBJECT_OPERATOR === $this->tokens[ $previous_non_empty ]['code']
1857+
|| \T_DOUBLE_COLON === $this->tokens[ $previous_non_empty ]['code']
1858+
) {
1859+
// Method call.
1860+
continue 2;
1861+
}
1862+
1863+
if ( \T_NS_SEPARATOR === $this->tokens[ $previous_non_empty ]['code'] ) {
1864+
$pprev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $previous_non_empty - 1 ), null, true, null, true );
1865+
if ( false !== $pprev && \T_STRING === $this->tokens[ $pprev ]['code'] ) {
1866+
// Namespaced function call.
1867+
continue 2;
1868+
}
1869+
}
1870+
}
1871+
1872+
$params = $this->get_function_call_parameters( $i );
1873+
if ( $params[2]['raw'] !== $this->tokens[ $stackPtr ]['content'] ) {
1874+
continue 2;
1875+
}
1876+
1877+
if ( isset( $array_key )
1878+
&& $this->strip_quotes( $params[1]['raw'] ) !== $bare_array_key ) {
1879+
continue 2;
1880+
}
1881+
1882+
return true;
18241883
}
18251884
}
18261885

0 commit comments

Comments
 (0)