diff --git a/classes/helpers/FrmAppHelper.php b/classes/helpers/FrmAppHelper.php
index ecd0b6f186..0c42de2875 100644
--- a/classes/helpers/FrmAppHelper.php
+++ b/classes/helpers/FrmAppHelper.php
@@ -1167,6 +1167,8 @@ private static function allowed_html( $allowed ) {
/**
* @since 2.05.03
+ *
+ * @return array
*/
private static function safe_html() {
$allow_class = array(
diff --git a/classes/helpers/FrmEntriesListHelper.php b/classes/helpers/FrmEntriesListHelper.php
index a7121f6fc8..b157de81c1 100644
--- a/classes/helpers/FrmEntriesListHelper.php
+++ b/classes/helpers/FrmEntriesListHelper.php
@@ -17,6 +17,9 @@ class FrmEntriesListHelper extends FrmListHelper {
*/
public $total_items = 0;
+ /**
+ * @param array $args
+ */
public function __construct( $args ) {
parent::__construct( $args );
$this->screen->set_screen_reader_content(
diff --git a/classes/helpers/FrmFormsListHelper.php b/classes/helpers/FrmFormsListHelper.php
index fdf5a85983..0277cfc59c 100644
--- a/classes/helpers/FrmFormsListHelper.php
+++ b/classes/helpers/FrmFormsListHelper.php
@@ -12,6 +12,9 @@ class FrmFormsListHelper extends FrmListHelper {
public $total_items = 0;
+ /**
+ * @param array $args
+ */
public function __construct( $args ) {
$this->status = self::get_param( array( 'param' => 'form_type' ) );
diff --git a/classes/helpers/FrmListHelper.php b/classes/helpers/FrmListHelper.php
index 19c21052fc..11d9177c3e 100644
--- a/classes/helpers/FrmListHelper.php
+++ b/classes/helpers/FrmListHelper.php
@@ -162,6 +162,9 @@ public function ajax_user_can() {
return current_user_can( 'administrator' );
}
+ /**
+ * @return array
+ */
public function get_columns() {
return array();
}
diff --git a/classes/models/FrmOnSubmitAction.php b/classes/models/FrmOnSubmitAction.php
index a8f1d956e2..afba42dc46 100644
--- a/classes/models/FrmOnSubmitAction.php
+++ b/classes/models/FrmOnSubmitAction.php
@@ -50,6 +50,9 @@ public function form( $instance, $args = array() ) {
include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/on_submit_settings.php';
}
+ /**
+ * @return array
+ */
public function get_defaults() {
return array(
'success_action' => FrmOnSubmitHelper::get_default_action_type(),
diff --git a/classes/models/fields/FrmFieldCheckbox.php b/classes/models/fields/FrmFieldCheckbox.php
index 95c9dc271a..c32579bcf1 100644
--- a/classes/models/fields/FrmFieldCheckbox.php
+++ b/classes/models/fields/FrmFieldCheckbox.php
@@ -116,6 +116,10 @@ protected function show_readonly_hidden() {
return true;
}
+ /**
+ * @param array $atts
+ * @param mixed $value
+ */
protected function prepare_import_value( $value, $atts ) {
return $this->get_multi_opts_for_import( $value );
}
diff --git a/classes/models/fields/FrmFieldNumber.php b/classes/models/fields/FrmFieldNumber.php
index 0596b4d306..8a6711324e 100644
--- a/classes/models/fields/FrmFieldNumber.php
+++ b/classes/models/fields/FrmFieldNumber.php
@@ -59,6 +59,9 @@ protected function add_extra_html_atts( $args, &$input_html ) {
$this->add_min_max( $args, $input_html );
}
+ /**
+ * @param array $args
+ */
public function validate( $args ) {
$errors = array();
diff --git a/classes/models/fields/FrmFieldSelect.php b/classes/models/fields/FrmFieldSelect.php
index a80e3fd1e4..95ecb8e792 100644
--- a/classes/models/fields/FrmFieldSelect.php
+++ b/classes/models/fields/FrmFieldSelect.php
@@ -91,6 +91,10 @@ protected function show_readonly_hidden() {
return true;
}
+ /**
+ * @param array $atts
+ * @param mixed $value
+ */
protected function prepare_import_value( $value, $atts ) {
if ( FrmField::is_option_true( $this->field, 'multiple' ) ) {
$value = $this->get_multi_opts_for_import( $value );
diff --git a/classes/models/fields/FrmFieldText.php b/classes/models/fields/FrmFieldText.php
index 3449867868..bd9bb9bbe8 100644
--- a/classes/models/fields/FrmFieldText.php
+++ b/classes/models/fields/FrmFieldText.php
@@ -39,6 +39,9 @@ protected function field_settings_for_type() {
);
}
+ /**
+ * @param array $args
+ */
public function validate( $args ) {
$errors = parent::validate( $args );
$max_length = intval( FrmField::get_option( $this->field, 'max' ) );
diff --git a/classes/models/fields/FrmFieldTextarea.php b/classes/models/fields/FrmFieldTextarea.php
index d735a4eec6..84124e0ee3 100644
--- a/classes/models/fields/FrmFieldTextarea.php
+++ b/classes/models/fields/FrmFieldTextarea.php
@@ -72,6 +72,10 @@ public function show_on_form_builder( $name = '' ) {
echo '';
}
+ /**
+ * @param array $atts
+ * @param mixed $value
+ */
protected function prepare_display_value( $value, $atts ) {
FrmFieldsHelper::run_wpautop( $atts, $value );
return $value;
diff --git a/classes/models/fields/FrmFieldUrl.php b/classes/models/fields/FrmFieldUrl.php
index 75a175c616..a59a167d71 100644
--- a/classes/models/fields/FrmFieldUrl.php
+++ b/classes/models/fields/FrmFieldUrl.php
@@ -65,6 +65,9 @@ protected function fill_default_atts( &$atts ) {
}
}
+ /**
+ * @param array $args
+ */
public function validate( $args ) {
$value = $args['value'];
@@ -89,6 +92,10 @@ public function validate( $args ) {
return $errors;
}
+ /**
+ * @param array $atts
+ * @param mixed $value
+ */
protected function prepare_display_value( $value, $atts ) {
if ( ! $atts['html'] ) {
return $value;
diff --git a/classes/models/fields/FrmFieldUserID.php b/classes/models/fields/FrmFieldUserID.php
index 1ec0b2c0b8..e7a7f38fb5 100644
--- a/classes/models/fields/FrmFieldUserID.php
+++ b/classes/models/fields/FrmFieldUserID.php
@@ -48,6 +48,9 @@ protected function include_form_builder_file() {
return FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/field-user-id.php';
}
+ /**
+ * @param array $args
+ */
public function prepare_field_html( $args ) {
$args = $this->fill_display_field_values( $args );
$value = $this->get_field_value( $args );
@@ -71,6 +74,11 @@ protected function get_field_value( $args ) {
return is_numeric( $this->field['value'] ) || $posted_value || $updating ? $this->field['value'] : $user_ID;
}
+ /**
+ * @param array $args
+ *
+ * @return array
+ */
public function validate( $args ) {
// phpcs:ignore Universal.Operators.StrictComparisons
if ( '' == $args['value'] ) {
diff --git a/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingTypeCommentsSniff.php b/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingTypeCommentsSniff.php
new file mode 100644
index 0000000000..f986f4ea57
--- /dev/null
+++ b/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingTypeCommentsSniff.php
@@ -0,0 +1,549 @@
+getTokens();
+
+ // Skip if no scope (abstract method, interface method).
+ if ( ! isset( $tokens[ $stackPtr ]['scope_opener'] ) || ! isset( $tokens[ $stackPtr ]['scope_closer'] ) ) {
+ return;
+ }
+
+ // Check if function has a docblock.
+ $docblock = $this->findDocblock( $phpcsFile, $stackPtr );
+
+ // Get function parameters.
+ $params = $this->getFunctionParams( $phpcsFile, $stackPtr );
+
+ // Get return type info.
+ $returnType = $this->detectReturnType( $phpcsFile, $stackPtr );
+
+ // Check for params that need @param array ($args, $atts).
+ $missingParamDocs = $this->getMissingParamDocs( $phpcsFile, $docblock, $params );
+
+ // Check for missing @return.
+ $missingReturn = $this->getMissingReturn( $phpcsFile, $docblock, $returnType );
+
+ if ( empty( $missingParamDocs ) && empty( $missingReturn ) ) {
+ return;
+ }
+
+ // Build the error message.
+ $errorParts = array();
+
+ if ( ! empty( $missingParamDocs ) ) {
+ $errorParts[] = '@param array for: ' . implode( ', ', array_keys( $missingParamDocs ) );
+ }
+
+ if ( ! empty( $missingReturn ) ) {
+ $errorParts[] = '@return ' . $missingReturn;
+ }
+
+ $errorMessage = 'Missing type comments: ' . implode( '; ', $errorParts );
+
+ $fix = $phpcsFile->addFixableError(
+ $errorMessage,
+ $stackPtr,
+ 'MissingTypeComments'
+ );
+
+ if ( true === $fix ) {
+ $this->addMissingDocblock( $phpcsFile, $stackPtr, $docblock, $missingParamDocs, $missingReturn );
+ }
+ }
+
+ /**
+ * Find the docblock for a function.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ *
+ * @return false|int The docblock opener position, or false.
+ */
+ private function findDocblock( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ $ignore = array(
+ T_WHITESPACE,
+ T_STATIC,
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_ABSTRACT,
+ T_FINAL,
+ );
+
+ $prev = $phpcsFile->findPrevious( $ignore, $stackPtr - 1, null, true );
+
+ if ( false !== $prev && $tokens[ $prev ]['code'] === T_DOC_COMMENT_CLOSE_TAG ) {
+ return $tokens[ $prev ]['comment_opener'];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get function parameters.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ *
+ * @return array Array of parameter names.
+ */
+ private function getFunctionParams( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+ $params = array();
+
+ if ( ! isset( $tokens[ $stackPtr ]['parenthesis_opener'] ) || ! isset( $tokens[ $stackPtr ]['parenthesis_closer'] ) ) {
+ return $params;
+ }
+
+ $opener = $tokens[ $stackPtr ]['parenthesis_opener'];
+ $closer = $tokens[ $stackPtr ]['parenthesis_closer'];
+
+ for ( $i = $opener + 1; $i < $closer; $i++ ) {
+ if ( $tokens[ $i ]['code'] === T_VARIABLE ) {
+ $params[] = $tokens[ $i ]['content'];
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Detect the return type of a function.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ *
+ * @return string The detected return type ('array', 'string', or empty).
+ */
+ private function detectReturnType( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ $scopeOpener = $tokens[ $stackPtr ]['scope_opener'];
+ $scopeCloser = $tokens[ $stackPtr ]['scope_closer'];
+
+ $returnStatements = array();
+ $current = $scopeOpener;
+
+ while ( $current < $scopeCloser ) {
+ $return = $phpcsFile->findNext( T_RETURN, $current + 1, $scopeCloser );
+
+ if ( false === $return ) {
+ break;
+ }
+
+ // Check if this return is inside a nested closure/function.
+ if ( $this->isInsideNestedScope( $phpcsFile, $return, $stackPtr ) ) {
+ $current = $return;
+ continue;
+ }
+
+ $nextToken = $phpcsFile->findNext( T_WHITESPACE, $return + 1, null, true );
+
+ if ( false === $nextToken || $tokens[ $nextToken ]['code'] === T_SEMICOLON ) {
+ // Empty return.
+ $returnStatements[] = 'void';
+ } elseif ( $tokens[ $nextToken ]['code'] === T_ARRAY || $tokens[ $nextToken ]['code'] === T_OPEN_SHORT_ARRAY ) {
+ $returnStatements[] = 'array';
+ } elseif ( $tokens[ $nextToken ]['code'] === T_CONSTANT_ENCAPSED_STRING ) {
+ $returnStatements[] = 'string';
+ } else {
+ $returnStatements[] = 'unknown';
+ }
+
+ $current = $return;
+ }
+
+ if ( empty( $returnStatements ) ) {
+ return '';
+ }
+
+ // All returns must be the same type.
+ $uniqueTypes = array_unique( $returnStatements );
+
+ if ( count( $uniqueTypes ) === 1 ) {
+ $type = $uniqueTypes[0];
+
+ if ( 'array' === $type || 'string' === $type ) {
+ return $type;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Check if a token is inside a nested closure or function.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $tokenPtr The token to check.
+ * @param int $functionPtr The outer function token.
+ *
+ * @return bool
+ */
+ private function isInsideNestedScope( File $phpcsFile, $tokenPtr, $functionPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ if ( isset( $tokens[ $tokenPtr ]['conditions'] ) ) {
+ foreach ( $tokens[ $tokenPtr ]['conditions'] as $scopePtr => $scopeType ) {
+ if ( $scopePtr !== $functionPtr && in_array( $scopeType, array( T_CLOSURE, T_FUNCTION ), true ) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get parameters that need @param array documentation.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param false|int $docblock The docblock opener position, or false.
+ * @param array $params Array of parameter names.
+ *
+ * @return array Array of parameter names that need documentation.
+ */
+ private function getMissingParamDocs( File $phpcsFile, $docblock, $params ) {
+ $needsDocs = array();
+ $targetNames = array( '$args', '$atts' );
+
+ foreach ( $params as $param ) {
+ if ( ! in_array( $param, $targetNames, true ) ) {
+ continue;
+ }
+
+ // Check if already documented.
+ if ( false !== $docblock && $this->hasParamDoc( $phpcsFile, $docblock, $param ) ) {
+ continue;
+ }
+
+ $needsDocs[ $param ] = 'array';
+ }
+
+ return $needsDocs;
+ }
+
+ /**
+ * Check if a parameter is documented in the docblock.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $docblockStart The docblock opener position.
+ * @param string $paramName The parameter name to check.
+ *
+ * @return bool
+ */
+ private function hasParamDoc( File $phpcsFile, $docblockStart, $paramName ) {
+ $tokens = $phpcsFile->getTokens();
+ $docblockEnd = $tokens[ $docblockStart ]['comment_closer'];
+
+ for ( $i = $docblockStart; $i < $docblockEnd; $i++ ) {
+ if ( $tokens[ $i ]['code'] === T_DOC_COMMENT_TAG && $tokens[ $i ]['content'] === '@param' ) {
+ // Check the content after @param for the variable name.
+ $contentToken = $phpcsFile->findNext( T_DOC_COMMENT_STRING, $i + 1, $docblockEnd );
+
+ if ( false !== $contentToken && strpos( $tokens[ $contentToken ]['content'], $paramName ) !== false ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get missing @return type if applicable.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param false|int $docblock The docblock opener position, or false.
+ * @param string $returnType The detected return type.
+ *
+ * @return string The missing return type, or empty string.
+ */
+ private function getMissingReturn( File $phpcsFile, $docblock, $returnType ) {
+ if ( empty( $returnType ) ) {
+ return '';
+ }
+
+ // Check if already has @return.
+ if ( false !== $docblock && $this->hasReturnDoc( $phpcsFile, $docblock ) ) {
+ return '';
+ }
+
+ return $returnType;
+ }
+
+ /**
+ * Check if the docblock has a @return tag.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $docblockStart The docblock opener position.
+ *
+ * @return bool
+ */
+ private function hasReturnDoc( File $phpcsFile, $docblockStart ) {
+ $tokens = $phpcsFile->getTokens();
+ $docblockEnd = $tokens[ $docblockStart ]['comment_closer'];
+
+ for ( $i = $docblockStart; $i < $docblockEnd; $i++ ) {
+ if ( $tokens[ $i ]['code'] === T_DOC_COMMENT_TAG && $tokens[ $i ]['content'] === '@return' ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add or update the docblock with missing type comments.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ * @param false|int $docblock The existing docblock opener, or false.
+ * @param array $missingParamDocs Parameters needing documentation.
+ * @param string $missingReturn The missing return type.
+ *
+ * @return void
+ */
+ private function addMissingDocblock( File $phpcsFile, $stackPtr, $docblock, $missingParamDocs, $missingReturn ) {
+ $tokens = $phpcsFile->getTokens();
+ $fixer = $phpcsFile->fixer;
+
+ if ( false === $docblock ) {
+ // No existing docblock - create a new one.
+ $this->createNewDocblock( $phpcsFile, $stackPtr, $missingParamDocs, $missingReturn );
+ } else {
+ // Existing docblock - add missing entries.
+ $this->updateExistingDocblock( $phpcsFile, $docblock, $missingParamDocs, $missingReturn );
+ }
+ }
+
+ /**
+ * Create a new docblock for a function.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ * @param array $missingParamDocs Parameters needing documentation.
+ * @param string $missingReturn The missing return type.
+ *
+ * @return void
+ */
+ private function createNewDocblock( File $phpcsFile, $stackPtr, $missingParamDocs, $missingReturn ) {
+ $tokens = $phpcsFile->getTokens();
+ $fixer = $phpcsFile->fixer;
+
+ // Find the indentation of the function.
+ $indent = $this->getIndentation( $phpcsFile, $stackPtr );
+
+ // Build the docblock.
+ $docblock = $indent . "/**\n";
+
+ foreach ( $missingParamDocs as $paramName => $paramType ) {
+ $docblock .= $indent . " * @param {$paramType} {$paramName}\n";
+ }
+
+ if ( ! empty( $missingParamDocs ) && ! empty( $missingReturn ) ) {
+ $docblock .= $indent . " *\n";
+ }
+
+ if ( ! empty( $missingReturn ) ) {
+ $docblock .= $indent . " * @return {$missingReturn}\n";
+ }
+
+ $docblock .= $indent . " */\n";
+
+ // Find the position to insert (before any modifiers like public/private/static).
+ $insertPos = $this->findInsertPosition( $phpcsFile, $stackPtr );
+
+ $fixer->addContentBefore( $insertPos, $docblock );
+ }
+
+ /**
+ * Update an existing docblock with missing entries.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $docblockStart The docblock opener position.
+ * @param array $missingParamDocs Parameters needing documentation.
+ * @param string $missingReturn The missing return type.
+ *
+ * @return void
+ */
+ private function updateExistingDocblock( File $phpcsFile, $docblockStart, $missingParamDocs, $missingReturn ) {
+ $tokens = $phpcsFile->getTokens();
+ $fixer = $phpcsFile->fixer;
+ $docblockEnd = $tokens[ $docblockStart ]['comment_closer'];
+
+ // Find the indentation from the docblock.
+ $indent = $this->getIndentation( $phpcsFile, $docblockStart );
+
+ // Find the last @param or last tag position.
+ $lastParamPos = false;
+ $lastTagPos = false;
+
+ for ( $i = $docblockStart; $i < $docblockEnd; $i++ ) {
+ if ( $tokens[ $i ]['code'] === T_DOC_COMMENT_TAG ) {
+ $lastTagPos = $i;
+
+ if ( $tokens[ $i ]['content'] === '@param' ) {
+ $lastParamPos = $i;
+ }
+ }
+ }
+
+ // Add missing @param entries after the last @param or before closing.
+ if ( ! empty( $missingParamDocs ) ) {
+ $insertAfter = false !== $lastParamPos ? $this->findEndOfLine( $phpcsFile, $lastParamPos, $docblockEnd ) : false;
+
+ if ( false === $insertAfter ) {
+ $insertAfter = $docblockEnd - 1;
+ }
+
+ $paramContent = '';
+
+ foreach ( $missingParamDocs as $paramName => $paramType ) {
+ $paramContent .= "\n" . $indent . " * @param {$paramType} {$paramName}";
+ }
+
+ $fixer->addContent( $insertAfter, $paramContent );
+ }
+
+ // Add missing @return before the closing tag.
+ if ( ! empty( $missingReturn ) ) {
+ // We need to insert before the closing */ but on a new line.
+ // Find the whitespace token that contains the newline before */.
+ $beforeClose = $docblockEnd - 1;
+
+ if ( $tokens[ $beforeClose ]['code'] === T_DOC_COMMENT_WHITESPACE ) {
+ // Replace the whitespace with our new content plus proper closing.
+ // The existing whitespace already has a newline, so we start with the blank line marker.
+ $returnContent = $indent . " *\n" . $indent . " * @return {$missingReturn}\n" . $indent . ' ';
+ $fixer->replaceToken( $beforeClose, $returnContent );
+ }
+ }
+ }
+
+ /**
+ * Get the indentation for a token.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The token position.
+ *
+ * @return string The indentation string.
+ */
+ private function getIndentation( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find the start of the line.
+ $lineStart = $stackPtr;
+
+ while ( $lineStart > 0 && $tokens[ $lineStart - 1 ]['line'] === $tokens[ $stackPtr ]['line'] ) {
+ $lineStart--;
+ }
+
+ if ( $tokens[ $lineStart ]['code'] === T_WHITESPACE ) {
+ return $tokens[ $lineStart ]['content'];
+ }
+
+ return '';
+ }
+
+ /**
+ * Find the position to insert a new docblock.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The function token position.
+ *
+ * @return int The position to insert before.
+ */
+ private function findInsertPosition( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ $modifiers = array(
+ T_PUBLIC,
+ T_PRIVATE,
+ T_PROTECTED,
+ T_STATIC,
+ T_ABSTRACT,
+ T_FINAL,
+ );
+
+ $pos = $stackPtr;
+
+ while ( $pos > 0 ) {
+ $prev = $phpcsFile->findPrevious( T_WHITESPACE, $pos - 1, null, true );
+
+ if ( false === $prev || ! in_array( $tokens[ $prev ]['code'], $modifiers, true ) ) {
+ break;
+ }
+
+ $pos = $prev;
+ }
+
+ // Find the start of the line.
+ while ( $pos > 0 && $tokens[ $pos - 1 ]['line'] === $tokens[ $pos ]['line'] ) {
+ $pos--;
+ }
+
+ return $pos;
+ }
+
+ /**
+ * Find the end of a line in a docblock.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $startPos The starting position.
+ * @param int $docblockEnd The docblock end position.
+ *
+ * @return false|int The position at the end of the line, or false.
+ */
+ private function findEndOfLine( File $phpcsFile, $startPos, $docblockEnd ) {
+ $tokens = $phpcsFile->getTokens();
+ $currentLine = $tokens[ $startPos ]['line'];
+
+ for ( $i = $startPos; $i < $docblockEnd; $i++ ) {
+ if ( $tokens[ $i ]['line'] > $currentLine ) {
+ return $i - 1;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/phpcs-sniffs/Formidable/ruleset.xml b/phpcs-sniffs/Formidable/ruleset.xml
index b76dfb58e7..074056db8a 100644
--- a/phpcs-sniffs/Formidable/ruleset.xml
+++ b/phpcs-sniffs/Formidable/ruleset.xml
@@ -58,4 +58,5 @@