wpdb = $wpdb; $this->prefix = $wpdb->prefix; } public static function getInstance(): ContaiDatabase { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function getWpdb(): wpdb { return $this->wpdb; } public function getPrefix(): string { return $this->prefix; } public function getTableName(string $tableName): string { return $this->prefix . $tableName; } public function tableExists(string $tableName): bool { $table = $this->getTableName($tableName); $query = $this->wpdb->prepare('SHOW TABLES LIKE %s', $table); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $query is prepared on the line above. return $this->wpdb->get_var($query) === $table; } public function insert(string $table, array $data, array $format = []): int { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $result = $this->wpdb->insert($this->getTableName($table), $data, $format); if ($result === false) { contai_log("ContaiDatabase insert failed for table {$table}: " . $this->wpdb->last_error); return 0; } return $this->wpdb->insert_id; } public function update(string $table, array $data, array $where, array $format = [], array $whereFormat = []): int { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $this->wpdb->update( $this->getTableName($table), $data, $where, $format, $whereFormat ); } public function delete(string $table, array $where, array $whereFormat = []): int { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $this->wpdb->delete( $this->getTableName($table), $where, $whereFormat ); } public function query(string $query): bool { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query is prepared by callers via $this->prepare(). return $this->wpdb->query($query) !== false; } public function getResults(string $query, string $output = OBJECT): array { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query is prepared by callers via $this->prepare(). return $this->wpdb->get_results($query, $output) ?? []; } public function getRow(string $query, string $output = OBJECT) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query is prepared by callers via $this->prepare(). return $this->wpdb->get_row($query, $output); } public function getVar(string $query) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Query is prepared by callers via $this->prepare(). return $this->wpdb->get_var($query); } public function prepare(string $query, ...$args): string { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- This is the prepare() wrapper; $query is parameterized by callers. return $this->wpdb->prepare($query, ...$args); } public function getLastError(): string { return $this->wpdb->last_error; } public function getCharsetCollate(): string { return $this->wpdb->get_charset_collate(); } public function beginTransaction(): bool { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $this->wpdb->query('START TRANSACTION') !== false; } public function commit(): bool { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $this->wpdb->query('COMMIT') !== false; } public function rollback(): bool { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $this->wpdb->query('ROLLBACK') !== false; } private function __clone() {} public function __wakeup() { throw new Exception("Cannot unserialize singleton"); } }