You're mostly doing it right, but the Plugin Check Plugin (PCP) warning occurs because you're building the SQL string before passing it into $wpdb->prepare() — and this confuses static analyzers like Plugin Check.
Here’s the problem line:
$sql = "SELECT COUNT(*) FROM {$table1} WHERE section_course_id = %d";
$query = $db->wpdb->prepare( $sql, $post_id );
Even though $table1 is safe (likely a constant or controlled variable), tools like PCP expect everything except placeholders to be inside $wpdb->prepare() to enforce best practices.
Use sprintf() to inject the table name (since placeholders cannot be used for table names), then pass the resulting query string into $wpdb->prepare() with only values substituted through placeholders.
Fixed Code:
$db = STEPUP_Database::getInstance();
$table1 = $db->tb_lp_sections;
$sql = sprintf("SELECT COUNT(*) FROM %s WHERE section_course_id = %%d", $table1);
$query = $db->wpdb->prepare($sql, $post_id);
$result = $db->wpdb->get_var($query);
Note the double percent sign (%%d) inside sprintf() which escapes %d so that it remains available for $wpdb->prepare() to process.
Table names cannot be parameterized using %s inside $wpdb->prepare().
Therefore, use sprintf() to safely inject known table names.
Leave value placeholders (like %d, %s, etc.) to be handled by $wpdb->prepare() only.
This pattern satisfies both WordPress security practices and Plugin Check rules.
Never directly interpolate variables into SQL unless it’s a table name or column.
Always let $wpdb->prepare() handle data values using placeholders.
For table names, use sprintf(), then use $wpdb->prepare() for the rest.
WPDB::prepare() – WordPress Developer Docs