orm to frontend connected. for a time, i'll be trying multiple solutions at once.
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
class LoginCredentials
|
||||
{
|
||||
private string $username;
|
||||
private string $password;
|
||||
|
||||
public function __construct(string $username, string $password)
|
||||
{
|
||||
// More intensive SQL injection checks (example - adapt as needed)
|
||||
if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $password)) {
|
||||
throw new InvalidArgumentException('Invalid characters in username or password.');
|
||||
}
|
||||
// Consider using a more robust validation library
|
||||
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
||||
|
||||
class User
|
||||
{
|
||||
private int $userId;
|
||||
private string $username;
|
||||
private ?DateTime $createdAt;
|
||||
private ?DateTime $lastActive;
|
||||
private bool $isActive;
|
||||
private ?string $profilePicture;
|
||||
private ?string $bio;
|
||||
private ?string $website;
|
||||
|
||||
public function __construct(int $userId, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, ?mysqli $db = null)
|
||||
{
|
||||
// Basic XSS prevention on construction (can be enhanced)
|
||||
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
|
||||
$bio = htmlspecialchars($bio ?? '', ENT_QUOTES, 'UTF-8');
|
||||
$website = htmlspecialchars($website ?? '', ENT_QUOTES, 'UTF-8');
|
||||
$profilePicture = htmlspecialchars($profilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// Basic SQL injection prevention (should primarily rely on prepared statements)
|
||||
if (preg_match('/[\'";\-\_]/', $username) || preg_match('/[\'";\-\_]/', $bio) || preg_match('/[\'";\-\_]/', $website) || preg_match('/[\'";\-\_]/', $profilePicture)) {
|
||||
throw new InvalidArgumentException('Invalid characters in user data.');
|
||||
}
|
||||
|
||||
$this->userId = $userId;
|
||||
$this->username = $username;
|
||||
$this->profilePicture = $profilePicture;
|
||||
$this->bio = $bio;
|
||||
$this->website = $website;
|
||||
$this->createdAt = new DateTime();
|
||||
$this->lastActive = null;
|
||||
$this->isActive = false;
|
||||
|
||||
if ($db) {
|
||||
$stmt = $db->prepare('INSERT INTO user (user_id, username, profile_picture, bio, website) VALUES (?, ?, ?, ?, ?)');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('issss', $this->userId, $this->username, $this->profilePicture, $this->bio, $this->website);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
} else {
|
||||
// Consider logging or throwing an exception if no database connection is provided
|
||||
error_log('Database connection not provided during User creation.');
|
||||
}
|
||||
}
|
||||
|
||||
public static function exists(int $id)
|
||||
{
|
||||
$stmt = 'SELECT count(username) FROM user WHERE id = ?';
|
||||
echo pretty_dump(exec_stmt($stmt, 'i', $id)->fetch_assoc());
|
||||
}
|
||||
|
||||
public static function login(LoginCredentials $credentials, ?mysqli $db): ?User
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for login.');
|
||||
return null;
|
||||
}
|
||||
$username = $credentials->getUsername();
|
||||
$password = $credentials->getPassword();
|
||||
|
||||
$stmt = $db->prepare('SELECT user_id, username, password_hash, profile_picture, bio, website FROM user WHERE username = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('s', $username);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$user = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if ($user && hash('sha256', $password) === $user['password_hash']) {
|
||||
return new User(
|
||||
(int) $user['user_id'],
|
||||
$user['username'],
|
||||
$user['profile_picture'],
|
||||
$user['bio'],
|
||||
$user['website']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function logout(): void
|
||||
{
|
||||
foreach (array_keys($_SESSION) as $key) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
// Consider destroying the session cookie as well: session_destroy();
|
||||
header('Location: .'); // Redirect to the homepage or login page
|
||||
exit();
|
||||
}
|
||||
|
||||
public function getUserId(): int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getLastActive(): ?DateTime
|
||||
{
|
||||
return $this->lastActive;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->isActive;
|
||||
}
|
||||
|
||||
public function getProfilePicture(): ?string
|
||||
{
|
||||
return $this->profilePicture;
|
||||
}
|
||||
|
||||
public function getBio(): ?string
|
||||
{
|
||||
return $this->bio;
|
||||
}
|
||||
|
||||
public function getWebsite(): ?string
|
||||
{
|
||||
return $this->website;
|
||||
}
|
||||
|
||||
public function setLastActive(?DateTime $lastActive): void
|
||||
{
|
||||
$this->lastActive = $lastActive;
|
||||
}
|
||||
|
||||
public function setIsActive(bool $isActive): void
|
||||
{
|
||||
$this->isActive = $isActive;
|
||||
}
|
||||
|
||||
public function toggleActive(?mysqli $db): bool
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for toggleActive.');
|
||||
return false;
|
||||
}
|
||||
$this->isActive = !$this->isActive;
|
||||
$stmt = $db->prepare('UPDATE user SET is_active = ? WHERE user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('ii', (int) $this->isActive, $this->userId);
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateUsername(string $newUsername, ?mysqli $db): bool
|
||||
{
|
||||
$newUsername = htmlspecialchars($newUsername, ENT_QUOTES, 'UTF-8');
|
||||
if (preg_match('/[\'";\-\_]/', $newUsername)) {
|
||||
throw new InvalidArgumentException('Invalid characters in username.');
|
||||
}
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for updateUsername.');
|
||||
return false;
|
||||
}
|
||||
$stmt = $db->prepare('UPDATE user SET username = ? WHERE user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('si', $newUsername, $this->userId);
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
if ($result) {
|
||||
$this->username = $newUsername;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updatePassword(string $newPassword, ?mysqli $db): bool
|
||||
{
|
||||
$newPasswordHash = hash('sha256', $newPassword);
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for updatePassword.');
|
||||
return false;
|
||||
}
|
||||
$stmt = $db->prepare('UPDATE user SET password_hash = ? WHERE user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('si', $newPasswordHash, $this->userId);
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateProfilePicture(?string $newProfilePicture, ?mysqli $db): bool
|
||||
{
|
||||
$newProfilePicture = htmlspecialchars($newProfilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
|
||||
if (preg_match('/[\'";\-\_]/', $newProfilePicture)) {
|
||||
throw new InvalidArgumentException('Invalid characters in profile picture filename.');
|
||||
}
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for updateProfilePicture.');
|
||||
return false;
|
||||
}
|
||||
$stmt = $db->prepare('UPDATE user SET profile_picture = ? WHERE user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('si', $newProfilePicture, $this->userId);
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
if ($result) {
|
||||
$this->profilePicture = $newProfilePicture;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function followUser(int $followingUserId, bool $notify = false, ?mysqli $db): bool
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for followUser.');
|
||||
return false;
|
||||
}
|
||||
$stmt = $db->prepare('INSERT INTO follows (follower_user_id, following_user_id, notify_user) VALUES (?, ?, ?)');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('iii', $this->userId, $followingUserId, (int) $notify);
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFollowers(?mysqli $db): array
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for getFollowers.');
|
||||
return [];
|
||||
}
|
||||
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.follower_user_id = u.user_id WHERE f.following_user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('i', $this->userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$followers = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$followers[] = new User(
|
||||
(int) $row['user_id'],
|
||||
$row['username'],
|
||||
$row['profile_picture'],
|
||||
$row['bio'],
|
||||
$row['website']
|
||||
);
|
||||
}
|
||||
$stmt->close();
|
||||
return $followers;
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function getFollowing(?mysqli $db): array
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for getFollowing.');
|
||||
return [];
|
||||
}
|
||||
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.following_user_id = u.user_id WHERE f.follower_user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('i', $this->userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$following = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$following[] = new User(
|
||||
(int) $row['user_id'],
|
||||
$row['username'],
|
||||
$row['profile_picture'],
|
||||
$row['bio'],
|
||||
$row['website']
|
||||
);
|
||||
}
|
||||
$stmt->close();
|
||||
return $following;
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Static method to fetch a User by ID (example using mysqli)
|
||||
public static function findById(int $userId, ?mysqli $db): ?User
|
||||
{
|
||||
if (!$db) {
|
||||
error_log('Database connection not provided for findById.');
|
||||
return null;
|
||||
}
|
||||
$stmt = $db->prepare('SELECT user_id, username, profile_picture, bio, website FROM user WHERE user_id = ?');
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('i', $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$user = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
if ($user) {
|
||||
return new User(
|
||||
(int) $user['user_id'],
|
||||
$user['username'],
|
||||
$user['profile_picture'],
|
||||
$user['bio'],
|
||||
$user['website']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error_log('Error preparing statement: ' . $db->error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Author extends User {}
|
||||
Reference in New Issue
Block a user