PHP版本控制系统:高效文档管理

系统功能说明

这个PHP版本控制系统实现了以下核心功能:

1. 用户认证与权限管理

  • 用户注册、登录和登出
  • 三级权限系统:管理员(admin)、编辑者(editor)和查看者(viewer)
  • 基于角色的访问控制(RBAC)

2. 仓库管理

  • 创建新仓库
  • 查看仓库列表和详情
  • 仓库统计信息(提交数、分支数、贡献者)

3. 文档版本控制

  • 上传文档到仓库
  • 查看文档历史版本
  • 检索特定版本的文档内容
  • 类似Git的提交机制

4. 分支管理

  • 创建新分支
  • 查看分支列表
  • 合并分支到主分支

5. 权限控制

  • 仓库级别的权限控制
  • 基于用户角色的操作限制

安装与使用

  1. 创建数据库并运行install.php初始化数据库结构
  2. 配置config.php中的数据库连接信息
  3. 将所有文件上传到Web服务器
  4. 访问系统首页进行注册和登录
  5. 管理员可以创建仓库,用户可以上传文档并管理版本

这个系统提供了完整的文档版本控制功能,适合团队协作管理文档,支持权限分级和版本历史追踪。

以下是一些视图文件的示例,用于展示用户界面:

css 复制代码
	/* css/style.css */
	* {
	    margin: 0;
	    padding: 0;
	    box-sizing: border-box;
	}
	body {
	    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
	    line-height: 1.6;
	    color: #333;
	    background-color: #f5f5f5;
	}
	.container {
	    max-width: 1200px;
	    margin: 0 auto;
	    padding: 0 20px;
	}
	header {
	    background-color: #2c3e50;
	    color: white;
	    padding: 1rem 0;
	    margin-bottom: 2rem;
	}
	header h1 {
	    display: inline-block;
	    margin-right: 2rem;
	}
	header nav {
	    display: inline-block;
	}
	header nav a {
	    color: white;
	    text-decoration: none;
	    margin-right: 1rem;
	    padding: 0.5rem;
	    border-radius: 4px;
	    transition: background-color 0.3s;
	}
	header nav a:hover {
	    background-color: #34495e;
	}
	main {
	    background-color: white;
	    border-radius: 8px;
	    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
	    padding: 2rem;
	    margin-bottom: 2rem;
	}
	footer {
	    text-align: center;
	    padding: 1rem 0;
	    color: #7f8c8d;
	    font-size: 0.9rem;
	}
	/* Home Page */
	.hero {
	    text-align: center;
	    padding: 3rem 0;
	    background-color: #3498db;
	    color: white;
	    border-radius: 8px;
	    margin-bottom: 2rem;
	}
	.hero h2 {
	    font-size: 2.5rem;
	    margin-bottom: 1rem;
	}
	.hero p {
	    font-size: 1.2rem;
	    margin-bottom: 2rem;
	    max-width: 800px;
	    margin-left: auto;
	    margin-right: auto;
	}
	.cta a {
	    display: inline-block;
	    margin: 0 0.5rem;
	    padding: 0.75rem 1.5rem;
	    border-radius: 4px;
	    text-decoration: none;
	    font-weight: bold;
	}
	.btn {
	    background-color: #2ecc71;
	    color: white;
	}
	.btn-secondary {
	    background-color: #95a5a6;
	    color: white;
	}
	.features {
	    display: flex;
	    justify-content: space-between;
	    margin-top: 2rem;
	}
	.feature {
	    flex: 1;
	    padding: 1.5rem;
	    margin: 0 0.5rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
	}
	.feature h3 {
	    margin-bottom: 1rem;
	    color: #2c3e50;
	}
	/* Dashboard */
	.dashboard-header {
	    margin-bottom: 2rem;
	    padding-bottom: 1rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.dashboard-header h2 {
	    margin-bottom: 0.5rem;
	}
	.repositories h3 {
	    margin-bottom: 1rem;
	}
	.repo-list {
	    display: grid;
	    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
	    gap: 1.5rem;
	}
	.repo-card {
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    padding: 1.5rem;
	    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
	    transition: transform 0.3s, box-shadow 0.3s;
	}
	.repo-card:hover {
	    transform: translateY(-5px);
	    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
	}
	.repo-card h4 {
	    margin-bottom: 0.5rem;
	}
	.repo-card h4 a {
	    color: #3498db;
	    text-decoration: none;
	}
	.repo-card h4 a:hover {
	    text-decoration: underline;
	}
	.repo-meta {
	    margin-top: 1rem;
	    font-size: 0.9rem;
	    color: #7f8c8d;
	}
	.repo-meta span {
	    margin-right: 1rem;
	}
	/* Repository Page */
	.repo-header {
	    margin-bottom: 2rem;
	    padding-bottom: 1rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.repo-header h2 {
	    margin-bottom: 0.5rem;
	}
	.repo-actions {
	    margin-top: 1rem;
	}
	.repo-actions a {
	    margin-right: 0.5rem;
	}
	.repo-stats {
	    display: flex;
	    margin-bottom: 2rem;
	}
	.stat {
	    flex: 1;
	    padding: 1rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    text-align: center;
	    margin: 0 0.5rem;
	}
	.stat h4 {
	    margin-bottom: 0.5rem;
	    color: #7f8c8d;
	}
	.stat p {
	    font-size: 1.5rem;
	    font-weight: bold;
	    color: #2c3e50;
	}
	.repo-latest-commit, .repo-branches, .repo-documents {
	    margin-bottom: 2rem;
	}
	.repo-latest-commit h3, .repo-branches h3, .repo-documents h3 {
	    margin-bottom: 1rem;
	    padding-bottom: 0.5rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.commit {
	    padding: 1rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	}
	.commit-message {
	    margin-bottom: 0.5rem;
	    font-weight: bold;
	}
	.commit-meta {
	    font-size: 0.9rem;
	    color: #7f8c8d;
	}
	.commit-meta span {
	    margin-right: 1rem;
	}
	.repo-branches ul {
	    list-style-type: none;
	}
	.repo-branches li {
	    padding: 0.5rem 0;
	    border-bottom: 1px solid #ecf0f1;
	}
	.document-list {
	    width: 100%;
	    border-collapse: collapse;
	}
	.document-list th, .document-list td {
	    padding: 0.75rem;
	    text-align: left;
	    border-bottom: 1px solid #ecf0f1;
	}
	.document-list th {
	    background-color: #f8f9fa;
	    font-weight: bold;
	}
	.document-list tr:hover {
	    background-color: #f8f9fa;
	}
	/* Forms */
	.form-container {
	    max-width: 600px;
	    margin: 0 auto;
	}
	.form-container h2 {
	    margin-bottom: 1.5rem;
	    text-align: center;
	}
	.form-group {
	    margin-bottom: 1.5rem;
	}
	.form-group label {
	    display: block;
	    margin-bottom: 0.5rem;
	    font-weight: bold;
	}
	.form-group input[type="text"],
	.form-group input[type="email"],
	.form-group input[type="password"],
	.form-group input[type="file"],
	.form-group textarea {
	    width: 100%;
	    padding: 0.75rem;
	    border: 1px solid #ddd;
	    border-radius: 4px;
	    font-size: 1rem;
	}
	.form-group textarea {
	    resize: vertical;
	}
	.form-group small {
	    display: block;
	    margin-top: 0.25rem;
	    color: #7f8c8d;
	    font-size: 0.9rem;
	}
	.form-actions {
	    text-align: center;
	    margin-top: 2rem;
	}
	.form-actions button {
	    margin: 0 0.5rem;
	}
	.error {
	    padding: 1rem;
	    margin-bottom: 1rem;
	    background-color: #f8d7da;
	    color: #721c24;
	    border-radius: 4px;
	    border: 1px solid #f5c6cb;
	}
	/* Responsive Design */
	@media (max-width: 768px) {
	    header {
	        text-align: center;
	    }
	    header h1 {
	        display: block;
	        margin-bottom: 1rem;
	    }
	    .features {
	        flex-direction: column;
	    }
	    .feature {
	        margin: 0.5rem 0;
	    }
	    .repo-stats {
	        flex-direction: column;
	    }
	    .stat {
	        margin: 0.5rem 0;
	    }
	    .repo-list {
	        grid-template-columns: 1fr;
	    }
	}

view

html 复制代码
	<!-- views/upload.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Upload Document - <?php echo htmlspecialchars($repository['name']); ?></title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <a href="index.php?action=repo&id=<?php echo $repository['id']; ?>">Repository</a>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="form-container">
	                <h2>Upload Document</h2>
	                <?php if (isset($error)): ?>
	                    <div class="error"><?php echo htmlspecialchars($error); ?></div>
	                <?php endif; ?>
	                <form action="index.php?action=upload&id=<?php echo $repository['id']; ?>" method="post" enctype="multipart/form-data">
	                    <div class="form-group">
	                        <label for="file">Select Document</label>
	                        <input type="file" id="file" name="file" required>
	                        <small>Allowed file types: <?php echo implode(', ', Config::ALLOWED_EXTENSIONS); ?></small>
	                    </div>
	                    <div class="form-group">
	                        <label for="message">Commit Message</label>
	                        <textarea id="message" name="message" rows="3"></textarea>
	                    </div>
	                    <div class="form-actions">
	                        <button type="submit" class="btn">Upload Document</button>
	                        <a href="index.php?action=repo&id=<?php echo $repository['id']; ?>" class="btn btn-secondary">Cancel</a>
	                    </div>
	                </form>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
html 复制代码
	<!-- views/repo.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title><?php echo htmlspecialchars($repository['name']); ?> - Document VCS</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="repo-header">
	                <h2><?php echo htmlspecialchars($repository['name']); ?></h2>
	                <p><?php echo htmlspecialchars($repository['description']); ?></p>
	                <div class="repo-actions">
	                    <?php if ($auth->hasPermission('write', $repository['id'])): ?>
	                        <a href="index.php?action=upload&id=<?php echo $repository['id']; ?>" class="btn">Upload Document</a>
	                        <a href="index.php?action=create_branch&repo_id=<?php echo $repository['id']; ?>" class="btn">Create Branch</a>
	                        <a href="index.php?action=merge_branch&repo_id=<?php echo $repository['id']; ?>" class="btn">Merge Branch</a>
	                    <?php endif; ?>
	                </div>
	            </div>
	            <div class="repo-stats">
	                <div class="stat">
	                    <h4>Commits</h4>
	                    <p><?php echo $stats['commit_count']; ?></p>
	                </div>
	                <div class="stat">
	                    <h4>Branches</h4>
	                    <p><?php echo $stats['branch_count']; ?></p>
	                </div>
	                <div class="stat">
	                    <h4>Contributors</h4>
	                    <p><?php echo count($stats['contributors']); ?></p>
	                </div>
	            </div>
	            <div class="repo-latest-commit">
	                <h3>Latest Commit</h3>
	                <?php if (isset($repository['latest_commit'])): ?>
	                    <div class="commit">
	                        <p class="commit-message"><?php echo htmlspecialchars($repository['latest_commit']['message']); ?></p>
	                        <div class="commit-meta">
	                            <span>Hash: <?php echo substr($repository['latest_commit']['hash'], 0, 7); ?></span>
	                            <span>Author: <?php echo htmlspecialchars($repository['latest_commit']['author']); ?></span>
	                            <span>Date: <?php echo $repository['latest_commit']['date']; ?></span>
	                        </div>
	                    </div>
	                <?php else: ?>
	                    <p>No commits yet</p>
	                <?php endif; ?>
	            </div>
	            <div class="repo-branches">
	                <h3>Branches</h3>
	                <ul>
	                    <?php foreach ($repository['branches'] as $branch): ?>
	                        <li><?php echo htmlspecialchars($branch); ?></li>
	                    <?php endforeach; ?>
	                </ul>
	            </div>
	            <div class="repo-documents">
	                <h3>Documents</h3>
	                <?php if (empty($documents)): ?>
	                    <p>No documents found.</p>
	                <?php else: ?>
	                    <table class="document-list">
	                        <thead>
	                            <tr>
	                                <th>Name</th>
	                                <th>Actions</th>
	                            </tr>
	                        </thead>
	                        <tbody>
	                            <?php foreach ($documents as $doc): ?>
	                                <tr>
	                                    <td><?php echo htmlspecialchars($doc['name']); ?></td>
	                                    <td>
	                                        <a href="index.php?action=history&repo_id=<?php echo $repository['id']; ?>&file=<?php echo urlencode($doc['name']); ?>">View History</a>
	                                    </td>
	                                </tr>
	                            <?php endforeach; ?>
	                        </tbody>
	                    </table>
	                <?php endif; ?>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
html 复制代码
	<!-- views/dashboard.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Dashboard - Document VCS</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <?php if ($auth->getUser()['role'] === Config::ROLE_ADMIN): ?>
	                    <a href="index.php?action=create_repo">Create Repository</a>
	                <?php endif; ?>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="dashboard-header">
	                <h2>Welcome, <?php echo htmlspecialchars($auth->getUser()['username']); ?></h2>
	                <p>Role: <?php echo ucfirst($auth->getUser()['role']); ?></p>
	            </div>
	            <div class="repositories">
	                <h3>Your Repositories</h3>
	                <?php if (empty($repositories)): ?>
	                    <p>No repositories found. <?php if ($auth->getUser()['role'] === Config::ROLE_ADMIN): ?>
	                        <a href="index.php?action=create_repo">Create one</a>
	                    <?php endif; ?></p>
	                <?php else: ?>
	                    <div class="repo-list">
	                        <?php foreach ($repositories as $repo): ?>
	                            <div class="repo-card">
	                                <h4><a href="index.php?action=repo&id=<?php echo $repo['id']; ?>"><?php echo htmlspecialchars($repo['name']); ?></a></h4>
	                                <p><?php echo htmlspecialchars($repo['description']); ?></p>
	                                <div class="repo-meta">
	                                    <span>Created: <?php echo date('M j, Y', strtotime($repo['created_at'])); ?></span>
	                                    <span>Owner: <?php echo htmlspecialchars($repo['owner_id']); ?></span>
	                                </div>
	                            </div>
	                        <?php endforeach; ?>
	                    </div>
	                <?php endif; ?>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
html 复制代码
	<!-- views/home.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Document Version Control System</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <?php if ($auth->isLoggedIn()): ?>
	                    <a href="index.php?action=logout">Logout</a>
	                <?php else: ?>
	                    <a href="index.php?action=login">Login</a>
	                    <a href="index.php?action=register">Register</a>
	                <?php endif; ?>
	            </nav>
	        </header>
	        <main>
	            <div class="hero">
	                <h2>Welcome to Document VCS</h2>
	                <p>A powerful version control system for managing your documents with Git-like features.</p>
	                <?php if (!$auth->isLoggedIn()): ?>
	                    <div class="cta">
	                        <a href="index.php?action=login" class="btn">Login</a>
	                        <a href="index.php?action=register" class="btn btn-secondary">Register</a>
	                    </div>
	                <?php else: ?>
	                    <div class="cta">
	                        <a href="index.php?action=home" class="btn">Go to Dashboard</a>
	                    </div>
	                <?php endif; ?>
	            </div>
	            <div class="features">
	                <div class="feature">
	                    <h3>Version Control</h3>
	                    <p>Track changes, view history, and revert to previous versions of your documents.</p>
	                </div>
	                <div class="feature">
	                    <h3>Branching & Merging</h3>
	                    <p>Create branches for experimental changes and merge them back when ready.</p>
	                </div>
	                <div class="feature">
	                    <h3>Permission Management</h3>
	                    <p>Control who can view, edit, or administer your repositories with fine-grained permissions.</p>
	                </div>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
php 复制代码
	<?php
	// config.php - 配置文件
	class Config {
	    // 数据库配置
	    const DB_HOST = 'localhost';
	    const DB_NAME = 'document_vcs';
	    const DB_USER = 'root';
	    const DB_PASS = '';
	    // 文件存储路径
	    const REPO_PATH = __DIR__ . '/repositories/';
	    const UPLOAD_PATH = __DIR__ . '/uploads/';
	    // 权限级别
	    const ROLE_ADMIN = 'admin';      // 管理员:完全控制
	    const ROLE_EDITOR = 'editor';    // 编辑者:可编辑、提交
	    const ROLE_VIEWER = 'viewer';    // 查看者:只读权限
	    // 系统设置
	    const MAX_FILE_SIZE = 10485760;  // 10MB
	    const ALLOWED_EXTENSIONS = ['doc', 'docx', 'txt', 'pdf', 'md'];
	}
	// db.php - 数据库连接与操作
	class Database {
	    private static $instance = null;
	    private $connection;
	    private function __construct() {
	        try {
	            $this->connection = new PDO(
	                "mysql:host=" . Config::DB_HOST . ";dbname=" . Config::DB_NAME,
	                Config::DB_USER,
	                Config::DB_PASS
	            );
	            $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	        } catch (PDOException $e) {
	            die("Database connection failed: " . $e->getMessage());
	        }
	    }
	    public static function getInstance() {
	        if (!self::$instance) {
	            self::$instance = new self();
	        }
	        return self::$instance;
	    }
	    public function getConnection() {
	        return $this->connection;
	    }
	    public function query($sql, $params = []) {
	        try {
	            $stmt = $this->connection->prepare($sql);
	            $stmt->execute($params);
	            return $stmt;
	        } catch (PDOException $e) {
	            die("Query failed: " . $e->getMessage());
	        }
	    }
	    public function insert($table, $data) {
	        $columns = implode(', ', array_keys($data));
	        $placeholders = implode(', ', array_fill(0, count($data), '?'));
	        $sql = "INSERT INTO $table ($columns) VALUES ($placeholders)";
	        $this->query($sql, array_values($data));
	        return $this->connection->lastInsertId();
	    }
	    public function update($table, $data, $where, $whereParams = []) {
	        $setParts = [];
	        foreach ($data as $key => $value) {
	            $setParts[] = "$key = ?";
	        }
	        $setClause = implode(', ', $setParts);
	        $sql = "UPDATE $table SET $setClause WHERE $where";
	        $params = array_merge(array_values($data), $whereParams);
	        $this->query($sql, $params);
	        return $this->connection->rowCount();
	    }
	    public function delete($table, $where, $params = []) {
	        $sql = "DELETE FROM $table WHERE $where";
	        $this->query($sql, $params);
	        return $this->connection->rowCount();
	    }
	    public function select($table, $where = '', $params = [], $columns = '*') {
	        $sql = "SELECT $columns FROM $table";
	        if ($where) {
	            $sql .= " WHERE $where";
	        }
	        return $this->query($sql, $params)->fetchAll(PDO::FETCH_ASSOC);
	    }
	    public function selectOne($table, $where = '', $params = [], $columns = '*') {
	        $sql = "SELECT $columns FROM $table";
	        if ($where) {
	            $sql .= " WHERE $where";
	        }
	        $result = $this->query($sql, $params)->fetch(PDO::FETCH_ASSOC);
	        return $result ?: null;
	    }
	}
	// auth.php - 用户认证与权限管理
	class Auth {
	    private $db;
	    private $user = null;
	    public function __construct() {
	        $this->db = Database::getInstance();
	        session_start();
	        if (isset($_SESSION['user_id'])) {
	            $this->user = $this->db->selectOne(
	                'users', 
	                'id = ?', 
	                [$_SESSION['user_id']]
	            );
	        }
	    }
	    public function login($username, $password) {
	        $user = $this->db->selectOne(
	            'users', 
	            'username = ?', 
	            [$username]
	        );
	        if ($user && password_verify($password, $user['password'])) {
	            $_SESSION['user_id'] = $user['id'];
	            $this->user = $user;
	            return true;
	        }
	        return false;
	    }
	    public function logout() {
	        session_destroy();
	        $this->user = null;
	    }
	    public function register($username, $email, $password, $role = Config::ROLE_VIEWER) {
	        // 检查用户名是否已存在
	        if ($this->db->selectOne('users', 'username = ?', [$username])) {
	            throw new Exception("Username already exists");
	        }
	        // 检查邮箱是否已存在
	        if ($this->db->selectOne('users', 'email = ?', [$email])) {
	            throw new Exception("Email already exists");
	        }
	        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
	        return $this->db->insert('users', [
	            'username' => $username,
	            'email' => $email,
	            'password' => $hashedPassword,
	            'role' => $role,
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	    }
	    public function isLoggedIn() {
	        return $this->user !== null;
	    }
	    public function getUser() {
	        return $this->user;
	    }
	    public function hasPermission($permission, $documentId = null) {
	        if (!$this->isLoggedIn()) {
	            return false;
	        }
	        // 管理员拥有所有权限
	        if ($this->user['role'] === Config::ROLE_ADMIN) {
	            return true;
	        }
	        // 检查文档特定权限
	        if ($documentId) {
	            $docPermission = $this->db->selectOne(
	                'document_permissions',
	                'document_id = ? AND user_id = ?',
	                [$documentId, $this->user['id']]
	            );
	            if ($docPermission) {
	                return $docPermission['permission'] === $permission;
	            }
	        }
	        // 默认权限检查
	        switch ($permission) {
	            case 'read':
	                return in_array($this->user['role'], [Config::ROLE_EDITOR, Config::ROLE_VIEWER]);
	            case 'write':
	                return $this->user['role'] === Config::ROLE_EDITOR;
	            case 'admin':
	                return false;
	            default:
	                return false;
	        }
	    }
	    public function requireLogin() {
	        if (!$this->isLoggedIn()) {
	            header('Location: login.php');
	            exit;
	        }
	    }
	    public function requirePermission($permission, $documentId = null) {
	        $this->requireLogin();
	        if (!$this->hasPermission($permission, $documentId)) {
	            http_response_code(403);
	            die("You don't have permission to perform this action");
	        }
	    }
	}
	// repository.php - 仓库管理与版本控制
	class Repository {
	    private $db;
	    private $auth;
	    private $repoPath;
	    public function __construct($auth) {
	        $this->db = Database::getInstance();
	        $this->auth = $auth;
	        $this->repoPath = Config::REPO_PATH;
	        // 确保仓库目录存在
	        if (!file_exists($this->repoPath)) {
	            mkdir($this->repoPath, 0755, true);
	        }
	    }
	    public function createRepository($name, $description = '') {
	        $this->auth->requirePermission('admin');
	        // 检查仓库名称是否已存在
	        if ($this->db->selectOne('repositories', 'name = ?', [$name])) {
	            throw new Exception("Repository already exists");
	        }
	        // 创建数据库记录
	        $repoId = $this->db->insert('repositories', [
	            'name' => $name,
	            'description' => $description,
	            'owner_id' => $this->auth->getUser()['id'],
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	        // 创建仓库目录
	        $repoDir = $this->repoPath . $repoId;
	        if (!mkdir($repoDir, 0755, true)) {
	            throw new Exception("Failed to create repository directory");
	        }
	        // 初始化Git仓库
	        $this->executeGitCommand($repoDir, 'init');
	        $this->executeGitCommand($repoDir, 'config user.name "' . $this->auth->getUser()['username'] . '"');
	        $this->executeGitCommand($repoDir, 'config user.email "' . $this->auth->getUser()['email'] . '"');
	        // 创建初始提交
	        file_put_contents($repoDir . '/README.md', "# $name\n\n$description");
	        $this->executeGitCommand($repoDir, 'add README.md');
	        $this->executeGitCommand($repoDir, 'commit -m "Initial commit"');
	        return $repoId;
	    }
	    public function getRepositories() {
	        $this->auth->requireLogin();
	        $userId = $this->auth->getUser()['id'];
	        // 管理员可以查看所有仓库
	        if ($this->auth->getUser()['role'] === Config::ROLE_ADMIN) {
	            return $this->db->select('repositories');
	        }
	        // 其他用户只能查看有权限的仓库
	        return $this->db->select(
	            'repositories r',
	            'r.owner_id = ? OR EXISTS (
	                SELECT 1 FROM repository_permissions rp 
	                WHERE rp.repository_id = r.id AND rp.user_id = ?
	            )',
	            [$userId, $userId]
	        );
	    }
	    public function getRepository($id) {
	        $this->auth->requirePermission('read', $id);
	        $repo = $this->db->selectOne('repositories', 'id = ?', [$id]);
	        if (!$repo) {
	            throw new Exception("Repository not found");
	        }
	        $repoDir = $this->repoPath . $id;
	        // 获取最新提交信息
	        $latestCommit = $this->executeGitCommand($repoDir, 'log -1 --pretty=format:"%H|%s|%an|%ad" --date=short');
	        if ($latestCommit) {
	            list($hash, $message, $author, $date) = explode('|', $latestCommit);
	            $repo['latest_commit'] = [
	                'hash' => $hash,
	                'message' => $message,
	                'author' => $author,
	                'date' => $date
	            ];
	        }
	        // 获取分支列表
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        $branches = [];
	        if ($branchesOutput) {
	            $lines = explode("\n", trim($branchesOutput));
	            foreach ($lines as $line) {
	                $branch = trim(str_replace('*', '', $line));
	                $branches[] = $branch;
	            }
	        }
	        $repo['branches'] = $branches;
	        return $repo;
	    }
	    public function addDocument($repoId, $file, $message = '') {
	        $this->auth->requirePermission('write', $repoId);
	        // 验证文件
	        if ($file['error'] !== UPLOAD_ERR_OK) {
	            throw new Exception("File upload error");
	        }
	        if ($file['size'] > Config::MAX_FILE_SIZE) {
	            throw new Exception("File too large");
	        }
	        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
	        if (!in_array($extension, Config::ALLOWED_EXTENSIONS)) {
	            throw new Exception("File type not allowed");
	        }
	        $repoDir = $this->repoPath . $repoId;
	        $fileName = $file['name'];
	        $filePath = $repoDir . '/' . $fileName;
	        // 移动文件到仓库目录
	        if (!move_uploaded_file($file['tmp_name'], $filePath)) {
	            throw new Exception("Failed to save file");
	        }
	        // 添加到Git并提交
	        $this->executeGitCommand($repoDir, 'add "' . $fileName . '"');
	        $commitMessage = $message ?: "Add document: $fileName";
	        $this->executeGitCommand($repoDir, 'commit -m "' . $commitMessage . '"');
	        // 记录到数据库
	        $commitHash = trim($this->executeGitCommand($repoDir, 'rev-parse HEAD'));
	        $this->db->insert('documents', [
	            'repository_id' => $repoId,
	            'name' => $fileName,
	            'file_path' => $filePath,
	            'commit_hash' => $commitHash,
	            'uploaded_by' => $this->auth->getUser()['id'],
	            'uploaded_at' => date('Y-m-d H:i:s')
	        ]);
	        return $commitHash;
	    }
	    public function getDocuments($repoId) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取Git中的文件列表
	        $filesOutput = $this->executeGitCommand($repoDir, 'ls-files');
	        $files = [];
	        if ($filesOutput) {
	            $lines = explode("\n", trim($filesOutput));
	            foreach ($lines as $line) {
	                $files[] = [
	                    'name' => $line,
	                    'path' => $repoDir . '/' . $line
	                ];
	            }
	        }
	        return $files;
	    }
	    public function getDocumentHistory($repoId, $fileName) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取文件历史记录
	        $historyOutput = $this->executeGitCommand(
	            $repoDir, 
	            'log --pretty=format:"%H|%s|%an|%ad" --date=short -- "' . $fileName . '"'
	        );
	        $history = [];
	        if ($historyOutput) {
	            $lines = explode("\n", trim($historyOutput));
	            foreach ($lines as $line) {
	                list($hash, $message, $author, $date) = explode('|', $line);
	                $history[] = [
	                    'hash' => $hash,
	                    'message' => $message,
	                    'author' => $author,
	                    'date' => $date
	                ];
	            }
	        }
	        return $history;
	    }
	    public function getDocumentVersion($repoId, $fileName, $commitHash) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取特定版本的文件内容
	        $content = $this->executeGitCommand($repoDir, 'show ' . $commitHash . ':"' . $fileName . '"');
	        return $content;
	    }
	    public function createBranch($repoId, $branchName, $sourceBranch = 'main') {
	        $this->auth->requirePermission('write', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 检查分支是否已存在
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        if ($branchesOutput && strpos($branchesOutput, $branchName) !== false) {
	            throw new Exception("Branch already exists");
	        }
	        // 创建新分支
	        $this->executeGitCommand($repoDir, 'branch ' . $branchName . ' ' . $sourceBranch);
	        // 记录到数据库
	        $this->db->insert('branches', [
	            'repository_id' => $repoId,
	            'name' => $branchName,
	            'source_branch' => $sourceBranch,
	            'created_by' => $this->auth->getUser()['id'],
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	        return true;
	    }
	    public function mergeBranch($repoId, $sourceBranch, $targetBranch = 'main') {
	        $this->auth->requirePermission('write', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 切换到目标分支
	        $this->executeGitCommand($repoDir, 'checkout ' . $targetBranch);
	        // 合并源分支
	        $mergeOutput = $this->executeGitCommand($repoDir, 'merge ' . $sourceBranch . ' --no-edit');
	        // 记录合并操作
	        $this->db->insert('merges', [
	            'repository_id' => $repoId,
	            'source_branch' => $sourceBranch,
	            'target_branch' => $targetBranch,
	            'merged_by' => $this->auth->getUser()['id'],
	            'merged_at' => date('Y-m-d H:i:s'),
	            'status' => 'success'
	        ]);
	        return $mergeOutput;
	    }
	    public function getRepositoryStats($repoId) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取提交总数
	        $commitCount = trim($this->executeGitCommand($repoDir, 'rev-list --count HEAD'));
	        // 获取分支数量
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        $branchCount = $branchesOutput ? count(explode("\n", trim($branchesOutput))) : 0;
	        // 获取贡献者数量
	        $contributorsOutput = $this->executeGitCommand($repoDir, 'shortlog -s | cut -c8-');
	        $contributors = [];
	        if ($contributorsOutput) {
	            $lines = explode("\n", trim($contributorsOutput));
	            foreach ($lines as $line) {
	                $contributors[] = trim($line);
	            }
	        }
	        return [
	            'commit_count' => (int)$commitCount,
	            'branch_count' => $branchCount,
	            'contributors' => $contributors
	        ];
	    }
	    private function executeGitCommand($repoDir, $command) {
	        $output = [];
	        $returnValue = 0;
	        $fullCommand = "cd $repoDir && git $command 2>&1";
	        exec($fullCommand, $output, $returnValue);
	        if ($returnValue !== 0) {
	            throw new Exception("Git command failed: " . implode("\n", $output));
	        }
	        return implode("\n", $output);
	    }
	}
	// install.php - 数据库安装脚本
	function installDatabase() {
	    $db = Database::getInstance();
	    // 创建用户表
	    $db->query("CREATE TABLE IF NOT EXISTS users (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        username VARCHAR(50) UNIQUE NOT NULL,
	        email VARCHAR(100) UNIQUE NOT NULL,
	        password VARCHAR(255) NOT NULL,
	        role ENUM('admin', 'editor', 'viewer') NOT NULL DEFAULT 'viewer',
	        created_at DATETIME NOT NULL
	    )");
	    // 创建仓库表
	    $db->query("CREATE TABLE IF NOT EXISTS repositories (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        name VARCHAR(100) UNIQUE NOT NULL,
	        description TEXT,
	        owner_id INT NOT NULL,
	        created_at DATETIME NOT NULL,
	        FOREIGN KEY (owner_id) REFERENCES users(id)
	    )");
	    // 创建文档表
	    $db->query("CREATE TABLE IF NOT EXISTS documents (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        name VARCHAR(255) NOT NULL,
	        file_path VARCHAR(255) NOT NULL,
	        commit_hash VARCHAR(40) NOT NULL,
	        uploaded_by INT NOT NULL,
	        uploaded_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (uploaded_by) REFERENCES users(id)
	    )");
	    // 创建分支表
	    $db->query("CREATE TABLE IF NOT EXISTS branches (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        name VARCHAR(100) NOT NULL,
	        source_branch VARCHAR(100) NOT NULL,
	        created_by INT NOT NULL,
	        created_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (created_by) REFERENCES users(id),
	        UNIQUE KEY (repository_id, name)
	    )");
	    // 创建合并记录表
	    $db->query("CREATE TABLE IF NOT EXISTS merges (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        source_branch VARCHAR(100) NOT NULL,
	        target_branch VARCHAR(100) NOT NULL,
	        merged_by INT NOT NULL,
	        merged_at DATETIME NOT NULL,
	        status ENUM('success', 'failed') NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (merged_by) REFERENCES users(id)
	    )");
	    // 创建仓库权限表
	    $db->query("CREATE TABLE IF NOT EXISTS repository_permissions (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        user_id INT NOT NULL,
	        permission ENUM('read', 'write', 'admin') NOT NULL,
	        granted_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (user_id) REFERENCES users(id),
	        UNIQUE KEY (repository_id, user_id)
	    )");
	    // 创建默认管理员账户
	    $adminExists = $db->selectOne('users', 'role = ?', [Config::ROLE_ADMIN]);
	    if (!$adminExists) {
	        $db->insert('users', [
	            'username' => 'admin',
	            'email' => 'admin@example.com',
	            'password' => password_hash('admin123', PASSWORD_DEFAULT),
	            'role' => Config::ROLE_ADMIN,
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	    }
	    return "Database installed successfully!";
	}
	// index.php - 主入口文件
	require_once 'config.php';
	require_once 'db.php';
	require_once 'auth.php';
	require_once 'repository.php';
	// 初始化
	$auth = new Auth();
	$repo = new Repository($auth);
	// 处理请求
	$action = $_GET['action'] ?? 'home';
	try {
	    switch ($action) {
	        case 'home':
	            // 首页
	            if ($auth->isLoggedIn()) {
	                $repositories = $repo->getRepositories();
	                include 'views/dashboard.php';
	            } else {
	                include 'views/home.php';
	            }
	            break;
	        case 'login':
	            // 登录
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $username = $_POST['username'] ?? '';
	                $password = $_POST['password'] ?? '';
	                if ($auth->login($username, $password)) {
	                    header('Location: index.php');
	                    exit;
	                } else {
	                    $error = "Invalid username or password";
	                    include 'views/login.php';
	                }
	            } else {
	                include 'views/login.php';
	            }
	            break;
	        case 'logout':
	            // 登出
	            $auth->logout();
	            header('Location: index.php');
	            break;
	        case 'register':
	            // 注册
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $username = $_POST['username'] ?? '';
	                $email = $_POST['email'] ?? '';
	                $password = $_POST['password'] ?? '';
	                $confirmPassword = $_POST['confirm_password'] ?? '';
	                if ($password !== $confirmPassword) {
	                    $error = "Passwords do not match";
	                    include 'views/register.php';
	                } else {
	                    try {
	                        $auth->register($username, $email, $password);
	                        $success = "Registration successful. Please login.";
	                        include 'views/login.php';
	                    } catch (Exception $e) {
	                        $error = $e->getMessage();
	                        include 'views/register.php';
	                    }
	                }
	            } else {
	                include 'views/register.php';
	            }
	            break;
	        case 'create_repo':
	            // 创建仓库
	            $auth->requirePermission('admin');
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $name = $_POST['name'] ?? '';
	                $description = $_POST['description'] ?? '';
	                try {
	                    $repoId = $repo->createRepository($name, $description);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    include 'views/create_repo.php';
	                }
	            } else {
	                include 'views/create_repo.php';
	            }
	            break;
	        case 'repo':
	            // 仓库详情
	            $repoId = $_GET['id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            $repository = $repo->getRepository($repoId);
	            $documents = $repo->getDocuments($repoId);
	            $stats = $repo->getRepositoryStats($repoId);
	            include 'views/repo.php';
	            break;
	        case 'upload':
	            // 上传文档
	            $repoId = $_GET['id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $message = $_POST['message'] ?? '';
	                try {
	                    $repo->addDocument($repoId, $_FILES['file'], $message);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/upload.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/upload.php';
	            }
	            break;
	        case 'history':
	            // 文档历史
	            $repoId = $_GET['repo_id'] ?? 0;
	            $fileName = $_GET['file'] ?? '';
	            if (!$repoId || !$fileName) {
	                header('Location: index.php');
	                exit;
	            }
	            $history = $repo->getDocumentHistory($repoId, $fileName);
	            $repository = $repo->getRepository($repoId);
	            include 'views/history.php';
	            break;
	        case 'version':
	            // 文档版本
	            $repoId = $_GET['repo_id'] ?? 0;
	            $fileName = $_GET['file'] ?? '';
	            $commitHash = $_GET['hash'] ?? '';
	            if (!$repoId || !$fileName || !$commitHash) {
	                header('Location: index.php');
	                exit;
	            }
	            $content = $repo->getDocumentVersion($repoId, $fileName, $commitHash);
	            $repository = $repo->getRepository($repoId);
	            include 'views/version.php';
	            break;
	        case 'create_branch':
	            // 创建分支
	            $repoId = $_GET['repo_id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $branchName = $_POST['name'] ?? '';
	                $sourceBranch = $_POST['source'] ?? 'main';
	                try {
	                    $repo->createBranch($repoId, $branchName, $sourceBranch);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/create_branch.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/create_branch.php';
	            }
	            break;
	        case 'merge_branch':
	            // 合并分支
	            $repoId = $_GET['repo_id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $sourceBranch = $_POST['source'] ?? '';
	                $targetBranch = $_POST['target'] ?? 'main';
	                try {
	                    $result = $repo->mergeBranch($repoId, $sourceBranch, $targetBranch);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/merge_branch.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/merge_branch.php';
	            }
	            break;
	        case 'install':
	            // 安装数据库
	            echo installDatabase();
	            break;
	        default:
	            // 默认首页
	            include 'views/home.php';
	            break;
	    }
	} catch (Exception $e) {
	    $error = $e->getMessage();
	    include 'views/error.php';
	}
	?>
相关推荐
JaguarJack9 小时前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay1 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954481 天前
CTF 伪协议
php
BingoGo4 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack4 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo5 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack5 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack6 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo6 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack7 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel