pdo = $pdo; $this->config = $config; } /** @return array> */ public function approvedFor(string $slug): array { $statement = $this->pdo->prepare( 'SELECT * FROM comments WHERE slug = :slug AND status = "approved" ORDER BY created_at ASC' ); $statement->execute(['slug' => $slug]); return $statement->fetchAll(); } /** @return array{ok: bool, stored: bool, message: string} */ public function add(string $slug, array $data): array { $honeypot = (string) $this->config->get('comments.honeypot_field', 'website_url'); if (trim((string) ($data[$honeypot] ?? '')) !== '') { return [ 'ok' => true, 'stored' => false, 'message' => 'Thanks. Your comment is awaiting review.', ]; } $author = trim((string) ($data['author'] ?? '')); $email = trim((string) ($data['email'] ?? '')); $website = trim((string) ($data['site_url'] ?? '')); $body = trim((string) ($data['body'] ?? '')); if ($author === '' || $email === '' || $body === '') { return ['ok' => false, 'stored' => false, 'message' => 'Name, email, and comment are required.']; } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { return ['ok' => false, 'stored' => false, 'message' => 'Please enter a valid email address.']; } if ($website !== '' && !filter_var($website, FILTER_VALIDATE_URL)) { return ['ok' => false, 'stored' => false, 'message' => 'Please enter a valid website URL or leave it blank.']; } if (mb_strlen($body) > 5000) { return ['ok' => false, 'stored' => false, 'message' => 'Comments must stay under 5,000 characters.']; } $now = date('c'); $status = $this->config->get('comments.moderate', true) ? 'pending' : 'approved'; $statement = $this->pdo->prepare( 'INSERT INTO comments (slug, author, email, website, body, status, ip_hash, user_agent, created_at, updated_at) VALUES (:slug, :author, :email, :website, :body, :status, :ip_hash, :user_agent, :created_at, :updated_at)' ); $statement->execute([ 'slug' => $slug, 'author' => $author, 'email' => $email, 'website' => $website, 'body' => $body, 'status' => $status, 'ip_hash' => $this->ipHash(), 'user_agent' => substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? 'cli'), 0, 300), 'created_at' => $now, 'updated_at' => $now, ]); return [ 'ok' => true, 'stored' => true, 'message' => $status === 'approved' ? 'Thanks. Your comment is live.' : 'Thanks. Your comment is awaiting review.', ]; } /** @param array $data */ public function import(string $slug, array $data): int { $createdAt = $this->normalizeDate((string) ($data['created_at'] ?? $data['date'] ?? '')); $updatedAt = $this->normalizeDate((string) ($data['updated_at'] ?? $data['modified'] ?? $createdAt)); $status = (string) ($data['status'] ?? 'approved'); if (!in_array($status, ['pending', 'approved', 'spam'], true)) { $status = match ($status) { 'published', 'published-comment', 'visible', '1', 'true' => 'approved', 'trash', 'deleted', 'spam-comment' => 'spam', default => 'pending', }; } $statement = $this->pdo->prepare( 'INSERT INTO comments (slug, author, email, website, body, status, ip_hash, user_agent, created_at, updated_at) VALUES (:slug, :author, :email, :website, :body, :status, :ip_hash, :user_agent, :created_at, :updated_at)' ); $statement->execute([ 'slug' => $slug, 'author' => trim((string) ($data['author'] ?? $data['name'] ?? 'Imported')), 'email' => trim((string) ($data['email'] ?? '')), 'website' => trim((string) ($data['website'] ?? $data['site_url'] ?? $data['url'] ?? '')), 'body' => trim((string) ($data['body'] ?? $data['content'] ?? $data['comment'] ?? '')), 'status' => $status, 'ip_hash' => trim((string) ($data['ip_hash'] ?? '')), 'user_agent' => substr((string) ($data['user_agent'] ?? $data['agent'] ?? 'import'), 0, 300), 'created_at' => $createdAt, 'updated_at' => $updatedAt, ]); return (int) $this->pdo->lastInsertId(); } /** @return array> */ public function list(?string $status = null, ?string $slug = null): array { $where = []; $params = []; if ($status !== null) { $where[] = 'status = :status'; $params['status'] = $status; } if ($slug !== null) { $where[] = 'slug = :slug'; $params['slug'] = $slug; } $sql = 'SELECT * FROM comments'; if ($where !== []) { $sql .= ' WHERE ' . implode(' AND ', $where); } $sql .= ' ORDER BY created_at DESC'; $statement = $this->pdo->prepare($sql); $statement->execute($params); return $statement->fetchAll(); } public function setStatus(int $id, string $status): bool { if (!in_array($status, ['pending', 'approved', 'spam'], true)) { return false; } $statement = $this->pdo->prepare('UPDATE comments SET status = :status, updated_at = :updated_at WHERE id = :id'); $statement->execute([ 'id' => $id, 'status' => $status, 'updated_at' => date('c'), ]); return $statement->rowCount() > 0; } public function delete(int $id): bool { $statement = $this->pdo->prepare('DELETE FROM comments WHERE id = :id'); $statement->execute(['id' => $id]); return $statement->rowCount() > 0; } private function ipHash(): string { $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? 'cli'); $salt = (string) $this->config->get('stats.hash_salt', 'change-me'); return hash_hmac('sha256', $ip, $salt); } private function normalizeDate(string $value): string { $timestamp = strtotime($value); return date('c', $timestamp ?: time()); } }