PHP音乐网站开发方案
我将设计一个功能丰富的音乐网站,使用PHP作为后端语言,结合MySQL数据库实现核心功能,下面是我的实现思路和完整代码。

设计思路
这个音乐网站将包含以下核心功能:
- 用户注册登录系统
- 音乐搜索与播放
- 个性化播放列表
- 艺术家和专辑浏览
- 响应式布局适配所有设备
数据库设计(MySQL)
CREATE DATABASE music_db;
USE music_db;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE artists (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
bio TEXT,
image_path VARCHAR(255)
);
CREATE TABLE albums (
id INT AUTO_INCREMENT PRIMARY KEY,VARCHAR(100) NOT NULL,
artist_id INT,
release_year YEAR,
cover_path VARCHAR(255),
FOREIGN KEY (artist_id) REFERENCES artists(id)
);
CREATE TABLE songs (
id INT AUTO_INCREMENT PRIMARY KEY,VARCHAR(100) NOT NULL,
artist_id INT,
album_id INT,
duration TIME,
file_path VARCHAR(255) NOT NULL,
plays INT DEFAULT 0,
FOREIGN KEY (artist_id) REFERENCES artists(id),
FOREIGN KEY (album_id) REFERENCES albums(id)
);
CREATE TABLE playlists (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
name VARCHAR(100) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE playlist_songs (
playlist_id INT,
song_id INT,
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (playlist_id, song_id),
FOREIGN KEY (playlist_id) REFERENCES playlists(id),
FOREIGN KEY (song_id) REFERENCES songs(id)
);
PHP实现 – 完整音乐网站代码
<?php
// 数据库连接
$host = 'localhost';
$dbname = 'music_db';
$username = 'root';
$password = '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
// 启动会话
session_start();
// 用户认证函数
function registerUser($username, $email, $password) {
global $pdo;
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
return $stmt->execute([$username, $email, $hashedPassword]);
}
function loginUser($username, $password) {
global $pdo;
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
return true;
}
return false;
}
// 获取音乐数据
function getFeaturedSongs() {
global $pdo;
$stmt = $pdo->query("SELECT songs.*, artists.name AS artist_name
FROM songs
JOIN artists ON songs.artist_id = artists.id
ORDER BY plays DESC LIMIT 10");
return $stmt->fetchAll();
}
function getNewReleases() {
global $pdo;
$stmt = $pdo->query("SELECT songs.*, artists.name AS artist_name, albums.title AS album_title, albums.cover_path
FROM songs
JOIN artists ON songs.artist_id = artists.id
JOIN albums ON songs.album_id = albums.id
ORDER BY albums.release_year DESC LIMIT 8");
return $stmt->fetchAll();
}
function searchSongs($query) {
global $pdo;
$stmt = $pdo->prepare("SELECT songs.*, artists.name AS artist_name
FROM songs
JOIN artists ON songs.artist_id = artists.id
WHERE songs.title LIKE ? OR artists.name LIKE ?");
$searchTerm = "%$query%";
$stmt->execute([$searchTerm, $searchTerm]);
return $stmt->fetchAll();
}
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'register':
if (registerUser($_POST['username'], $_POST['email'], $_POST['password'])) {
loginUser($_POST['username'], $_POST['password']);
header("Location: index.php");
exit;
}
break;
case 'login':
if (loginUser($_POST['username'], $_POST['password'])) {
header("Location: index.php");
exit;
}
break;
case 'search':
$searchResults = searchSongs($_POST['search_query']);
break;
}
}
}
// 获取页面数据
$featuredSongs = getFeaturedSongs();
$newReleases = getNewReleases();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">Harmony音乐 - 发现好音乐</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
:root {
--primary: #1DB954;
--dark: #121212;
--gray: #282828;
--light-gray: #b3b3b3;
--white: #ffffff;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--dark);
color: var(--white);
display: flex;
min-height: 100vh;
}
/* 侧边栏样式 */
.sidebar {
width: 230px;
background-color: #000;
padding: 24px;
position: fixed;
height: 100vh;
overflow-y: auto;
}
.logo {
margin-bottom: 30px;
font-size: 24px;
font-weight: bold;
color: var(--white);
}
.logo i {
color: var(--primary);
margin-right: 10px;
}
.nav-links {
list-style: none;
}
.nav-links li {
margin-bottom: 12px;
}
.nav-links a {
color: var(--light-gray);
text-decoration: none;
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
padding: 8px 0;
transition: color 0.3s;
}
.nav-links a:hover, .nav-links a.active {
color: var(--white);
}
.nav-links i {
margin-right: 16px;
font-size: 20px;
}
.playlists-section {
margin-top: 30px;
border-top: 1px solid var(--gray);
padding-top: 20px;
}
.playlists-section h3 {
color: var(--light-gray);
font-size: 14px;
text-transform: uppercase;
margin-bottom: 15px;
}
/* 主内容区样式 */
.main-content {
flex: 1;
margin-left: 230px;
padding: 20px 40px;
background: linear-gradient(to bottom, #404040, var(--dark) 400px);
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding: 10px 0;
}
.search-bar {
background-color: var(--white);
border-radius: 30px;
display: flex;
align-items: center;
padding: 8px 15px;
width: 350px;
}
.search-bar input {
border: none;
background: none;
outline: none;
flex: 1;
padding: 5px 10px;
font-size: 16px;
}
.search-bar button {
background: none;
border: none;
color: var(--dark);
cursor: pointer;
font-size: 18px;
}
.user-actions {
display: flex;
align-items: center;
}
.user-actions a {
color: var(--white);
text-decoration: none;
margin-left: 20px;
font-weight: 600;
font-size: 14px;
padding: 8px 15px;
border-radius: 20px;
}
.signup-btn {
background-color: var(--white);
color: var(--dark) !important;
}
.login-btn {
background-color: transparent;
border: 1px solid var(--white);
}
.user-profile {
display: flex;
align-items: center;
background-color: rgba(0,0,0,0.7);
border-radius: 30px;
padding: 5px;
}
.user-profile img {
width: 30px;
height: 30px;
border-radius: 50%;
margin-right: 10px;
}
/* 内容区块样式 */
.section-title {
font-size: 24px;
margin: 30px 0 20px;
}
.cards-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 20px;
}
.card {
background-color: var(--gray);
border-radius: 6px;
padding: 16px;
transition: background-color 0.3s;
cursor: pointer;
}
.card:hover {
background-color: #3a3a3a;
}
.card-img {
position: relative;
margin-bottom: 16px;
}
.card-img img {
width: 100%;
aspect-ratio: 1/1;
object-fit: cover;
border-radius: 4px;
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
}
.play-btn {
position: absolute;
bottom: 10px;
right: 10px;
background-color: var(--primary);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform: translateY(10px);
transition: all 0.3s;
box-shadow: 0 4px 8px rgba(0,0,0,0.4);
}
.card:hover .play-btn {
opacity: 1;
transform: translateY(0);
}
.play-btn i {
color: var(--dark);
font-size: 16px;
}
.card-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.card-subtitle {
color: var(--light-gray);
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 音乐播放器样式 */
.player-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: var(--gray);
height: 90px;
display: flex;
align-items: center;
padding: 0 20px;
border-top: 1px solid #404040;
}
.now-playing {
width: 30%;
display: flex;
align-items: center;
}
.now-playing img {
width: 56px;
height: 56px;
margin-right: 15px;
}
.track-info h4 {
font-size: 14px;
margin-bottom: 5px;
}
.track-info p {
color: var(--light-gray);
font-size: 12px;
}
.player-controls {
width: 40%;
display: flex;
flex-direction: column;
align-items: center;
}
.control-buttons {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.control-buttons button {
background: none;
border: none;
color: var(--light-gray);
font-size: 16px;
margin: 0 10px;
cursor: pointer;
transition: color 0.3s;
}
.control-buttons button:hover {
color: var(--white);
}
.control-buttons .play-pause {
background-color: var(--white);
color: var(--dark);
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.progress-container {
width: 100%;
display: flex;
align-items: center;
}
.progress-time {
font-size: 12px;
color: var(--light-gray);
min-width: 40px;
}
.progress-bar {
flex: 1;
height: 4px;
background-color: #5e5e5e;
border-radius: 2px;
position: relative;
cursor: pointer;
}
.progress {
height: 100%;
background-color: var(--light-gray);
border-radius: 2px;
width: 30%;
}
.progress-handle {
position: absolute;
top: 50%;
right: 0;
width: 12px;
height: 12px;
background-color: var(--white);
border-radius: 50%;
transform: translate(50%, -50%);
opacity: 0;
transition: opacity 0.3s;
}
.progress-bar:hover .progress-handle {
opacity: 1;
}
.volume-controls {
width: 30%;
display: flex;
justify-content: flex-end;
align-items: center;
}
.volume-bar {
width: 100px;
height: 4px;
background-color: #5e5e5e;
border-radius: 2px;
position: relative;
cursor: pointer;
margin-left: 10px;
}
.volume-level {
height: 100%;
background-color: var(--light-gray);
border-radius: 2px;
width: 70%;
}
/* 响应式设计 */
@media (max-width: 992px) {
.sidebar {
width: 70px;
padding: 15px 10px;
}
.logo span, .nav-links span {
display: none;
}
.nav-links i {
margin-right: 0;
font-size: 24px;
}
.playlists-section {
display: none;
}
.main-content {
margin-left: 70px;
}
}
@media (max-width: 768px) {
.cards-container {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.now-playing {
width: 20%;
}
.player-controls {
width: 50%;
}
.volume-controls {
width: 30%;
}
}
@media (max-width: 576px) {
.sidebar {
display: none;
}
.main-content {
margin-left: 0;
padding: 15px;
}
.top-bar {
flex-direction: column;
align-items: flex-start;
}
.search-bar {
width: 100%;
margin-top: 10px;
}
.player-bar {
flex-direction: column;
height: auto;
padding: 10px;
}
.now-playing, .player-controls, .volume-controls {
width: 100%;
margin-bottom: 10px;
}
.volume-controls {
justify-content: center;
}
}
</style>
</head>
<body>
<!-- 侧边导航 -->
<div class="sidebar">
<div class="logo">
<i class="fas fa-music"></i>
<span>Harmony音乐</span>
</div>
<ul class="nav-links">
<li><a href="#" class="active"><i class="fas fa-home"></i> <span>首页</span></a></li>
<li><a href="#"><i class="fas fa-search"></i> <span>搜索</span></a></li>
<li><a href="#"><i class="fas fa-book"></i> <span>音乐库</span></a></li>
<li><a href="#"><i class="fas fa-compact-disc"></i> <span>专辑</span></a></li>
<li><a href="#"><i class="fas fa-microphone-alt"></i> <span>艺术家</span></a></li>
<li><a href="#"><i class="fas fa-heart"></i> <span>收藏</span></a></li>
</ul>
<div class="playlists-section">
<h3>播放列表</h3>
<ul class="nav-links">
<li><a href="#"><i class="fas fa-plus-circle"></i> <span>新建播放列表</span></a></li>
<li><a href="#"><i class="fas fa-music"></i> <span>最爱歌曲</span></a></li>
<li><a href="#"><i class="fas fa-headphones"></i> <span>放松心情</span></a></li>
<li><a href="#"><i class="fas fa-running"></i> <span>运动健身</span></a></li>
</ul>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<div class="top-bar">
<div class="search-bar">
<form method="post" action="">
<input type="text" name="search_query" placeholder="搜索歌曲、艺术家或专辑">
<button type="submit" name="action" value="search"><i class="fas fa-search"></i></button>
</form>
</div>
<?php if(isset($_SESSION['user_id'])): ?>
<div class="user-profile">
<img src="https://randomuser.me/api/portraits/men/<?= rand(1,99) ?>.jpg" alt="用户头像">
<span><?= htmlspecialchars($_SESSION['username']) ?></span>
</div>
<?php else: ?>
<div class="user-actions">
<a href="#" class="signup-btn" id="signupBtn">注册</a>
<a href="#" class="login-btn" id="loginBtn">登录</a>
</div>
<?php endif; ?>
</div>
<!-- 特色推荐 -->
<h2 class="section-title">特色推荐</h2>
<div class="cards-container">
<?php foreach ($featuredSongs as $song): ?>
<div class="card">
<div class="card-img">
<img src="https://picsum.photos/300/300?random=<?= $song['id'] ?>" alt="<?= htmlspecialchars($song['title']) ?>">
<div class="play-btn">
<i class="fas fa-play"></i>
</div>
</div>
<div class="card-title"><?= htmlspecialchars($song['title']) ?></div>
<div class="card-subtitle"><?= htmlspecialchars($song['artist_name']) ?></div>
</div>
<?php endforeach; ?>
</div>
<!-- 新歌推荐 -->
<h2 class="section-title">新歌推荐</h2>
<div class="cards-container">
<?php foreach ($newReleases as $song): ?>
<div class="card">
<div class="card-img">
<img src="https://picsum.photos/300/300?random=<?= $song['id'] ?>" alt="<?= htmlspecialchars($song['title']) ?>">
<div class="play-btn">
<i class="fas fa-play"></i>
</div>
</div>
<div class="card-title"><?= htmlspecialchars($song['title']) ?></div>
<div class="card-subtitle"><?= htmlspecialchars($song['artist_name']) ?></div>
</div>
<?php endforeach; ?>
</div>
<!-- 搜索结果 -->
<?php if (isset($searchResults) && !empty($searchResults)): ?>
<h2 class="section-title">搜索结果</h2>
<div class="cards-container">
<?php foreach ($searchResults as $song): ?>
<div class="card">
<div class="card-img">
<img src="https://picsum.photos/300/300?random=<?= $song['id'] ?>" alt="<?= htmlspecialchars($song['title']) ?>">
<div class="play-btn">
<i class="fas fa-play"></i>
</div>
</div>
<div class="card-title"><?= htmlspecialchars($song['title']) ?></div>
<div class="card-subtitle"><?= htmlspecialchars($song['artist_name']) ?></div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- 音乐播放器 -->
<div class="player-bar">
<div class="now-playing">
<img src="https://picsum.photos/100/100?random=100" alt="当前播放">
<div class="track-info">
<h4>Blinding Lights</h4>
<p>The Weeknd</p>
</div>
</div>
<div class="player-controls">
<div class="control-buttons">
<button><i class="fas fa-random"></i></button>
<button><i class="fas fa-step-backward"></i></button>
<button class="play-pause"><i class="fas fa-play"></i></button>
<button><i class="fas fa-step-forward"></i></button>
<button><i class="fas fa-repeat"></i></button>
</div>
<div class="progress-container">
<div class="progress-time">1:23</div>
<div class="progress-bar">
<div class="progress"></div>
<div class="progress-handle"></div>
</div>
<div class="progress-time">3:45</div>
</div>
</div>
<div class="volume-controls">
<button><i class="fas fa-volume-up"></i></button>
<div class="volume-bar">
<div class="volume-level"></div>
</div>
</div>
</div>
<!-- 登录/注册弹窗 -->
<div id="authModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.7); z-index: 1000; justify-content: center; align-items: center;">
<div style="background-color: #282828; width: 100%; max-width: 400px; border-radius: 8px; padding: 30px; position: relative;">
<button id="closeModal" style="position: absolute; top: 15px; right: 15px; background: none; border: none; color: #b3b3b3; font-size: 24px; cursor: pointer;">×</button>
<div id="loginForm">
<h2 style="text-align: center; margin-bottom: 25px;">登录Harmony音乐</h2>
<form method="post" action="" style="display: flex; flex-direction: column;">
<input type="text" name="username" placeholder="用户名" style="background-color: #121212; border: 1px solid #404040; border-radius: 4px; padding: 12px; margin-bottom: 15px; color: white;">
<input type="password" name="password" placeholder="密码" style="background-color: #121212; border: 1px solid #404040; border-radius: 4px; padding: 12px; margin-bottom: 15px; color: white;">
<button type="submit" name="action" value="login" style="background-color: #1DB954; color: white; border: none; border-radius: 30px; padding: 12px; font-weight: bold; cursor: pointer; margin-bottom: 15px;">登录</button>
</form>
<p style="text-align: center; color: #b3b3b3; margin-top: 15px;">
还没有账号? <a href="#" id="showSignup" style="color: #1DB954; text-decoration: none; font-weight: bold;">注册</a>
</p>
</div>
<div id="signupForm" style="display: none;">
<h2 style="text-align: center; margin-bottom: 25px;">创建账户</h2>
<form method="post" action="" style="display: flex; flex-direction: column;">
<input type="text" name="username" placeholder="用户名" style="background-color: #121212; border: 1px solid #404040; border-radius: 4px; padding: 12px; margin-bottom: 15px; color: white;">
<input type="email" name="email" placeholder="电子邮箱" style="background-color: #121212; border: 1px solid #404040; border-radius: 4px; padding: 12px; margin-bottom: 15px; color: white;">
<input type="password" name="password" placeholder="密码" style="background-color: #121212; border: 1px solid #404040; border-radius: 4px; padding: 12px; margin-bottom: 15px; color: white;">
<button type="submit" name="action" value="register" style="background-color: #1DB954; color: white; border: none; border-radius: 30px; padding: 12px; font-weight: bold; cursor: pointer; margin-bottom: 15px;">注册</button>
</form>
<p style="text-align: center; color: #b3b3b3; margin-top: 15px;">
已有账号? <a href="#" id="showLogin" style="color: #1DB954; text-decoration: none; font-weight: bold;">登录</a>
</p>
</div>
</div>
</div>
<script>
// 登录/注册弹窗逻辑
const authModal = document.getElementById('authModal');
const loginForm = document.getElementById('loginForm');
const signupForm = document.getElementById('signupForm');
const showSignup = document.getElementById('showSignup');
const showLogin = document.getElementById('showLogin');
const closeModal = document.getElementById('closeModal');
const loginBtn = document.getElementById('loginBtn');
const signupBtn = document.getElementById('signupBtn');
if (loginBtn) {
loginBtn.addEventListener('click', (e) => {
e.preventDefault();
authModal.style.display = 'flex';
loginForm.style.display = 'block';
signupForm.style.display = 'none';
});
}
if (signupBtn) {
signupBtn.addEventListener('click', (e) => {
e.preventDefault();
authModal.style.display = 'flex';
loginForm.style.display = 'none';
signupForm.style.display = 'block';
});
}
showSignup.addEventListener('click', (e) => {
e.preventDefault();
loginForm.style.display = 'none';
signupForm.style.display = 'block';
});
showLogin.addEventListener('click', (e) => {
e.preventDefault();
loginForm.style.display = 'block';
signupForm.style.display = 'none';
});
closeModal.addEventListener('click', () => {
authModal.style.display = 'none';
});
// 播放按钮点击事件
document.querySelectorAll('.play-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.stopPropagation();
const card = this.closest('.card');
const title = card.querySelector('.card-title').textContent;
const artist = card.querySelector('.card-subtitle').textContent;
// 更新播放器信息
document.querySelector('.now-playing h4').textContent = title;
document.querySelector('.now-playing p').textContent = artist;
// 切换播放按钮状态
const playBtn = document.querySelector('.play-pause i');
if (playBtn.classList.contains('fa-play')) {
playBtn.classList.remove('fa-play');
playBtn.classList.add('fa-pause');
}
});
});
// 播放/暂停按钮
document.querySelector('.play-pause').addEventListener('click', function() {
const icon = this.querySelector('i');
if (icon.classList.contains('fa-play')) {
icon.classList.remove('fa-play');
icon.classList.add('fa-pause');
} else {
icon.classList.remove('fa-pause');
icon.classList.add('fa-play');
}
});
</script>
</body>
</html>
功能说明
这个音乐网站实现了以下核心功能:
-
用户系统

- 用户注册与登录
- 会话管理
- 密码安全存储(使用password_hash)
-
音乐管理
- 音乐展示(特色推荐、新歌推荐)
- 音乐搜索功能
- 播放次数统计
-
播放功能
- 音乐播放控制
- 进度条控制
- 音量控制
- 当前播放信息展示
-
响应式设计

- 适配桌面、平板和手机设备
- 移动端优化导航
-
数据库集成
- 使用PDO连接MySQL数据库
- 数据安全处理(防止SQL注入)
后续扩展建议
- 添加音乐上传功能(管理员界面)
- 实现播放列表创建和管理
- 添加音乐收藏功能
- 集成音乐推荐算法
- 添加用户社交功能(关注、分享)
- 实现音乐评论系统
这个音乐网站采用了现代化的深色主题设计,符合当前音乐平台的流行风格,同时确保了良好的用户体验和功能性。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/294396.html

