diff --git a/classes/models/FrmEntryMeta.php b/classes/models/FrmEntryMeta.php
index 72413bcfa9..4aced90fe6 100644
--- a/classes/models/FrmEntryMeta.php
+++ b/classes/models/FrmEntryMeta.php
@@ -393,7 +393,7 @@ public static function getAll( $where = array(), $order_by = '', $limit = '', $s
FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
$cache_key = 'all_' . FrmAppHelper::maybe_json_encode( $where ) . $order_by . $limit;
- $results = FrmDb::check_cache( $cache_key, 'frm_entry', $query, ( $limit == ' LIMIT 1' ? 'get_row' : 'get_results' ) ); // phpcs:ignore Universal.Operators.StrictComparisons, SlevomatCodingStandard.Files.LineLength.LineTooLong
+ $results = FrmDb::check_cache( $cache_key, 'frm_entry', $query, ( $limit === ' LIMIT 1' ? 'get_row' : 'get_results' ) );
if ( ! $results || ! $stripslashes ) {
return $results;
diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictComparisonSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictComparisonSniff.php
new file mode 100644
index 0000000000..964982aeaa
--- /dev/null
+++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictComparisonSniff.php
@@ -0,0 +1,164 @@
+getTokens();
+
+ // Check if there's a phpcs:ignore comment for this line.
+ if ( $this->hasIgnoreComment( $phpcsFile, $stackPtr ) ) {
+ return;
+ }
+
+ // Find the string literal on either side of the comparison.
+ $leftToken = $phpcsFile->findPrevious( T_WHITESPACE, $stackPtr - 1, null, true );
+ $rightToken = $phpcsFile->findNext( T_WHITESPACE, $stackPtr + 1, null, true );
+
+ if ( false === $leftToken || false === $rightToken ) {
+ return;
+ }
+
+ // Check if either side is a string literal.
+ $stringToken = false;
+
+ if ( $tokens[ $leftToken ]['code'] === T_CONSTANT_ENCAPSED_STRING ) {
+ $stringToken = $leftToken;
+ } elseif ( $tokens[ $rightToken ]['code'] === T_CONSTANT_ENCAPSED_STRING ) {
+ $stringToken = $rightToken;
+ }
+
+ if ( false === $stringToken ) {
+ return;
+ }
+
+ // Get the string value and check if it's safe for strict comparison.
+ $stringValue = $tokens[ $stringToken ]['content'];
+
+ if ( ! $this->isSafeStringForStrictComparison( $stringValue ) ) {
+ return;
+ }
+
+ // Remove quotes to get the actual string content for the message.
+ $stringContent = substr( $stringValue, 1, -1 );
+
+ // Determine the strict operator to use.
+ $isEqual = $tokens[ $stackPtr ]['code'] === T_IS_EQUAL;
+ $looseOperator = $isEqual ? '==' : '!=';
+ $strictOperator = $isEqual ? '===' : '!==';
+
+ $fix = $phpcsFile->addFixableError(
+ 'Use strict comparison (%s) instead of loose comparison (%s) when comparing to string "%s"',
+ $stackPtr,
+ 'Found',
+ array( $strictOperator, $looseOperator, $stringContent )
+ );
+
+ if ( true === $fix ) {
+ $phpcsFile->fixer->replaceToken( $stackPtr, $strictOperator );
+ }
+ }
+
+ /**
+ * Check if a string value is safe for strict comparison.
+ *
+ * A string is safe if it's:
+ * - Not empty
+ * - Not numeric (to avoid issues with '0' being falsy or numeric coercion)
+ *
+ * @param string $stringValue The string token content (including quotes).
+ *
+ * @return bool
+ */
+ private function isSafeStringForStrictComparison( $stringValue ) {
+ // Remove quotes.
+ $content = substr( $stringValue, 1, -1 );
+
+ // Empty string is not safe.
+ if ( $content === '' ) {
+ return false;
+ }
+
+ // Numeric strings are not safe (e.g., '0', '123', '1.5').
+ if ( is_numeric( $content ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if there's a phpcs:ignore comment for this line.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token.
+ *
+ * @return bool
+ */
+ private function hasIgnoreComment( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+ $line = $tokens[ $stackPtr ]['line'];
+
+ // Check the previous line for a phpcs:ignore comment.
+ for ( $i = $stackPtr - 1; $i >= 0; $i-- ) {
+ if ( $tokens[ $i ]['line'] < $line - 1 ) {
+ break;
+ }
+
+ if ( $tokens[ $i ]['line'] === $line - 1 || $tokens[ $i ]['line'] === $line ) {
+ if ( in_array( $tokens[ $i ]['code'], array( T_COMMENT, T_DOC_COMMENT_STRING ), true ) ) {
+ if ( strpos( $tokens[ $i ]['content'], 'phpcs:ignore' ) !== false ) {
+ // Check if it ignores StrictComparisons or our sniff.
+ if ( strpos( $tokens[ $i ]['content'], 'StrictComparisons' ) !== false ||
+ strpos( $tokens[ $i ]['content'], 'PreferStrictComparison' ) !== false ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictInArraySniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictInArraySniff.php
new file mode 100644
index 0000000000..2bbfc57798
--- /dev/null
+++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/PreferStrictInArraySniff.php
@@ -0,0 +1,324 @@
+getTokens();
+
+ // Check if this is an in_array call.
+ if ( strtolower( $tokens[ $stackPtr ]['content'] ) !== 'in_array' ) {
+ return;
+ }
+
+ // Check if there's a phpcs:ignore comment for this line.
+ if ( $this->hasIgnoreComment( $phpcsFile, $stackPtr ) ) {
+ return;
+ }
+
+ // Find the opening parenthesis.
+ $openParen = $phpcsFile->findNext( T_WHITESPACE, $stackPtr + 1, null, true );
+
+ if ( false === $openParen || $tokens[ $openParen ]['code'] !== T_OPEN_PARENTHESIS ) {
+ return;
+ }
+
+ if ( ! isset( $tokens[ $openParen ]['parenthesis_closer'] ) ) {
+ return;
+ }
+
+ $closeParen = $tokens[ $openParen ]['parenthesis_closer'];
+
+ // Parse the arguments.
+ $args = $this->parseArguments( $phpcsFile, $openParen, $closeParen );
+
+ // in_array needs at least 2 arguments.
+ if ( count( $args ) < 2 ) {
+ return;
+ }
+
+ // If there's already a third argument, skip.
+ if ( count( $args ) >= 3 ) {
+ return;
+ }
+
+ // Check if the second argument (haystack) is an array of safe strings.
+ $haystackArg = $args[1];
+
+ if ( ! $this->isArrayOfSafeStrings( $phpcsFile, $haystackArg['start'], $haystackArg['end'] ) ) {
+ return;
+ }
+
+ $fix = $phpcsFile->addFixableError(
+ 'Add strict mode (true) to in_array() when comparing against an array of strings',
+ $stackPtr,
+ 'Found'
+ );
+
+ if ( true === $fix ) {
+ $phpcsFile->fixer->beginChangeset();
+
+ // Find the position just before the closing parenthesis.
+ $insertPos = $phpcsFile->findPrevious( T_WHITESPACE, $closeParen - 1, null, true );
+
+ // Add ", true" after the last argument.
+ $phpcsFile->fixer->addContent( $insertPos, ', true' );
+
+ $phpcsFile->fixer->endChangeset();
+ }
+ }
+
+ /**
+ * Parse the arguments of a function call.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $openParen The position of the opening parenthesis.
+ * @param int $closeParen The position of the closing parenthesis.
+ *
+ * @return array Array of arguments, each with 'start' and 'end' positions.
+ */
+ private function parseArguments( File $phpcsFile, $openParen, $closeParen ) {
+ $tokens = $phpcsFile->getTokens();
+ $args = array();
+ $start = $openParen + 1;
+ $depth = 0;
+
+ for ( $i = $openParen + 1; $i < $closeParen; $i++ ) {
+ $code = $tokens[ $i ]['code'];
+
+ // Track nesting depth using bracket/paren closers when available.
+ if ( $code === T_OPEN_PARENTHESIS && isset( $tokens[ $i ]['parenthesis_closer'] ) ) {
+ ++$depth;
+ continue;
+ }
+
+ if ( $code === T_CLOSE_PARENTHESIS ) {
+ --$depth;
+ continue;
+ }
+
+ if ( $code === T_OPEN_SHORT_ARRAY && isset( $tokens[ $i ]['bracket_closer'] ) ) {
+ ++$depth;
+ continue;
+ }
+
+ if ( $code === T_CLOSE_SHORT_ARRAY ) {
+ --$depth;
+ continue;
+ }
+
+ if ( $code === T_OPEN_SQUARE_BRACKET ) {
+ ++$depth;
+ continue;
+ }
+
+ if ( $code === T_CLOSE_SQUARE_BRACKET ) {
+ --$depth;
+ continue;
+ }
+
+ // Comma at depth 0 separates arguments.
+ if ( $code === T_COMMA && $depth === 0 ) {
+ $args[] = array(
+ 'start' => $start,
+ 'end' => $i - 1,
+ );
+ $start = $i + 1;
+ }
+ }
+
+ // Add the last argument.
+ if ( $start < $closeParen ) {
+ $args[] = array(
+ 'start' => $start,
+ 'end' => $closeParen - 1,
+ );
+ }
+
+ return $args;
+ }
+
+ /**
+ * Check if the given range contains an array of safe strings.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $start The start position.
+ * @param int $end The end position.
+ *
+ * @return bool
+ */
+ private function isArrayOfSafeStrings( File $phpcsFile, $start, $end ) {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find the array start.
+ $arrayStart = $phpcsFile->findNext( T_WHITESPACE, $start, $end + 1, true );
+
+ if ( false === $arrayStart ) {
+ return false;
+ }
+
+ $arrayOpen = false;
+ $arrayClose = false;
+
+ // Check for short array syntax [ ].
+ if ( $tokens[ $arrayStart ]['code'] === T_OPEN_SHORT_ARRAY ) {
+ $arrayOpen = $arrayStart;
+
+ if ( isset( $tokens[ $arrayStart ]['bracket_closer'] ) ) {
+ $arrayClose = $tokens[ $arrayStart ]['bracket_closer'];
+ }
+ }
+
+ // Check for long array syntax array( ).
+ if ( $tokens[ $arrayStart ]['code'] === T_ARRAY ) {
+ $parenOpen = $phpcsFile->findNext( T_WHITESPACE, $arrayStart + 1, null, true );
+
+ if ( false !== $parenOpen && $tokens[ $parenOpen ]['code'] === T_OPEN_PARENTHESIS ) {
+ $arrayOpen = $parenOpen;
+
+ if ( isset( $tokens[ $parenOpen ]['parenthesis_closer'] ) ) {
+ $arrayClose = $tokens[ $parenOpen ]['parenthesis_closer'];
+ }
+ }
+ }
+
+ if ( false === $arrayOpen || false === $arrayClose ) {
+ return false;
+ }
+
+ // Check all elements in the array.
+ $hasElements = false;
+
+ for ( $i = $arrayOpen + 1; $i < $arrayClose; $i++ ) {
+ $code = $tokens[ $i ]['code'];
+
+ // Skip whitespace and commas.
+ if ( in_array( $code, array( T_WHITESPACE, T_COMMA ), true ) ) {
+ continue;
+ }
+
+ // Skip nested structures.
+ if ( in_array( $code, array( T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET ), true ) ) {
+ // Skip to the closing bracket.
+ if ( isset( $tokens[ $i ]['parenthesis_closer'] ) ) {
+ $i = $tokens[ $i ]['parenthesis_closer'];
+ } elseif ( isset( $tokens[ $i ]['bracket_closer'] ) ) {
+ $i = $tokens[ $i ]['bracket_closer'];
+ }
+ continue;
+ }
+
+ // Must be a string literal.
+ if ( $code !== T_CONSTANT_ENCAPSED_STRING ) {
+ return false;
+ }
+
+ // Check if the string is safe.
+ if ( ! $this->isSafeString( $tokens[ $i ]['content'] ) ) {
+ return false;
+ }
+
+ $hasElements = true;
+ }
+
+ // Must have at least one element.
+ return $hasElements;
+ }
+
+ /**
+ * Check if a string value is safe for strict comparison.
+ *
+ * @param string $stringValue The string token content (including quotes).
+ *
+ * @return bool
+ */
+ private function isSafeString( $stringValue ) {
+ // Remove quotes.
+ $content = substr( $stringValue, 1, -1 );
+
+ // Empty string is not safe.
+ if ( $content === '' ) {
+ return false;
+ }
+
+ // Numeric strings are not safe.
+ if ( is_numeric( $content ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if there's a phpcs:ignore comment for this line.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token.
+ *
+ * @return bool
+ */
+ private function hasIgnoreComment( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+ $line = $tokens[ $stackPtr ]['line'];
+
+ // Check the previous line for a phpcs:ignore comment.
+ for ( $i = $stackPtr - 1; $i >= 0; $i-- ) {
+ if ( $tokens[ $i ]['line'] < $line - 1 ) {
+ break;
+ }
+
+ if ( $tokens[ $i ]['line'] === $line - 1 || $tokens[ $i ]['line'] === $line ) {
+ if ( in_array( $tokens[ $i ]['code'], array( T_COMMENT, T_DOC_COMMENT_STRING ), true ) ) {
+ if ( strpos( $tokens[ $i ]['content'], 'phpcs:ignore' ) !== false ) {
+ // Check if it ignores StrictInArray or our sniff.
+ if ( strpos( $tokens[ $i ]['content'], 'StrictInArray' ) !== false ||
+ strpos( $tokens[ $i ]['content'], 'PreferStrictInArray' ) !== false ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/phpcs.xml b/phpcs.xml
index 45ff01337e..ea7e8302d6 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -251,4 +251,6 @@
+
+