[更新中] 创新实训项目

DeerOJ的前端框架介绍

Web文件夹下的结构

DeerOJ的前端框架参考了部分 Lavarel 框架,做到兼顾代码的可维护性和可阅读性。具体的维护目录文件结构如下:

注意到web文件夹下的 index.php 这是整个前端程序的 main 程序 ,当服务段收到请求后, 根据 .htaccess 文件指定使用 index.php 文件来生成网页,并把网页数据返回到前端进行渲染呈现。

App文件夹下的结构

网页的生成构造需要一些基本要素,web文件夹下的app文件夹中保存着大量的基本要素。app文件夹下的文件结构如下:

文件夹下包含一些子文件夹和文件,这里选取一些重要的文件来进行解析:

route.php文件

route.php 是整个前端程序的路由, index.php 会把前端发送的路由信息传递给route.php ,文件会根据发送的路由信息,结合 model 文件夹下的 Route.php 调用对应的php程序,从而来实现生成网页(HTML文件)的功能。具体解析如下:

路由模式定义

给出如下代码

这些模式定义了 URL 参数的格式。比如,username 只能包含字母、数字和下划线,长度在 1 到 20 之间;idcontest_id 是数字,长度在 1 到 10 之间;rand_str_id 是一个长度为 20 的字符串,由数字和大小写字母组成。

路由组定义


这个路由组限定了所有路由都在指定域名下可用。UOJConfig::$data['web']['main']['host'] 获取的是配置中的主机名。

具体路由配置


这些路由使用 Route::any 方法配置,表示无论是 GET 还是 POST 请求,都会路由到对应的 PHP 文件。例如,请求根路径 / 会被路由到 index.php 处理,请求 /problems 会被路由到 problem_set.php 处理。

用户相关路由


这些路由处理用户登录、注册、忘记密码、重置密码,以及查看和修改用户信息等操作。

评测相关路由


这些路由处理与评测系统相关的操作,例如提交评测任务、同步评测客户端、下载提交记录、临时文件、题目和评测器等。

config.php文件

config.php 文件是 PHP 应用程序的配置文件,具体用于 Deer Online Judge (DeerOJ) 系统的设置。它定义了应用程序的基本信息、数据库连接、Web 服务器设置、安全配置、邮件配置、评测机配置以及一些开关设置。

基本信息 (profile)


部分参数解释如下:

  • oj-name: 在线评测系统的全称。
  • oj-name-short: 在线评测系统的简称。
  • administrator: 系统管理员的用户名。
  • admin-email: 系统管理员的电子邮件地址。
  • QQ-group: QQ 群的联系方式,当前为空。
  • ICP-license: ICP 备案号,当前为空。

数据库配置 (database)


部分参数解释如下:

  • database: 数据库名称。
  • username: 数据库用户名。
  • password: 数据库用户的密码。
  • host: 数据库服务器的地址。

Web 服务器配置 (web)


部分参数解释如下:

  • domain: 网站的域名,当前为 NULL
  • main: 主站点的协议、主机名和端口。
  • blog: 博客站点的协议、主机名和端口。主机名是通过 UOJContext::httpHost() 获取的。

安全配置 (security)


部分参数解释如下:

  • user.client_salt: 用户密码的盐值,用于增强密码的安全性。
  • cookie.checksum_salt: 用于校验 Cookie 的盐值数组。

邮件配置 (mail)


部分参数解释如下:

  • noreply: 配置 no-reply 邮箱的用户名、密码、SMTP 服务器地址、加密方式和端口。

评测机配置 (judger)和开关设置 (switch)


部分参数解释如下:

  • socket.port: 评测机连接的端口号。
  • socket.password: 评测机连接的密码。
  • web-analytics: 是否启用网站分析,当前为 false(不启用)。
  • blog-domain-mode: 博客域名模式,当前为 3

model文件夹

model文件夹下存储的是一些相关类的php文件,在HTML文件生成的时候,利用这些类能够高效地辅助文件与文件之间的调度转换。文件夹下的内容如下:

这里列举一些重要的类文件:

Route.php文件

前文中在实现 route.php 的路由调度过程中有出现使用类Route的情况,实际上就是调用这里的类Route的相关方法去实现的。这个 Route 类是一个简单的路由器,它可以用于处理基于 URI 和 HTTP 方法的请求路由。它支持添加、匹配和分组路由,以及定义参数模式。下面介绍一下部分重要属性和方法:

Route.php文件的部分属性
  • protected static $routes: 存储所有注册的路由。
  • protected static $patterns: 存储所有自定义的参数模式。
  • protected static $groupStack: 存储当前的路由组属性。
match 方法

代码截图如下:

match方法将指定的 HTTP 方法、URI 和处理动作添加到路由中。其中,array_map('strtoupper', (array)$methods) 将方法名转为大写,便于规范匹配。

any 方法

代码截图如下:

any方法支持任意 HTTP 方法的路由,保证前端调用的时候能够保证所有的基本请求方式都能使用。

各种 HTTP 方法的快捷方式

代码截图如下:

将不同的 HTTP 方法打包在 Route类中,方便实现调用。

group 方法和 getGroup 方法

代码截图如下:

group 方法中,定义一个路由组,将传入的属性和当前组的属性合并,然后执行回调函数。其中,array_pop(self::$groupStack) 在执行完回调函数后恢复上一个路由组的属性。
getGroup 方法则是用于获取当前的路由组属性。

addRoute 方法

代码截图如下:

该方法将一个新路由添加到 $routes 数组中,便于后续对路由的检查。

checkRoute 方法

代码截图如下:

该方法检查当前请求是否匹配某个路由,并匹配请求方法。同时,使用定义的参数模式替换 URI 模式中的占位符。然后匹配 URI 和域名,如果匹配成功,将匹配的参数存储到 $_GET 中。

DB.php 文件

在编写代码的过程中,如果直接调用php中与数据库相关的语句,可能会导致代码可阅读性差,结构混乱从而加大了系统的维护成本。故使用 DB.php 来统一管理与数据库相关的交互操作。DB 类是一个封装了 MySQL 数据库操作的静态类,用于简化数据库操作并提高代码的可读性和可维护性。它使用 PHP 的 mysqli 扩展来与 MySQL 数据库进行交互。下面介绍一下部分重要属性和方法:

初始化数据库连接

初始化Init方法的具体代码如下:

该方法初始化与 MySQL 数据库的连接,并存储在全局变量 $uojMySQL 中。并且,使用 mysqli_connect 函数连接数据库,如果连接失败,输出错误信息并终止程序。

SQL 注入防护

escape 方法的具体代码如下:

该方法使用 mysqli_real_escape_string 函数对输入的字符串进行转义,以防止 SQL 注入攻击。

数据提取方法

fench 方法的具体代码如下:

该方法从查询结果集中获取一行作为关联数组或数字数组。

基本数据库操作

基本的增删改的实现,这里仅给出实现代码,代码如下:

数据选择方法

代码如下:

  • select 方法执行查询并返回结果。
  • selectAll 方法返回查询结果中的所有行。
  • selectFirst 方法返回查询结果中的第一行。
  • selectCount 方法返回查询结果的行数。
检查表是否存在

checkTableExists 方法的具体代码如下:

该方法通过尝试查询表中的一行数据来检查表是否存在。

行数和受影响行数

num_rowsaffected_rows 方法的具体代码实现如下:

其中,num_rows 方法返回结果集中行的数量,affected_rows 方法返回上一次查询中受影响的行数。

libs文件夹

index.php 文件初始化的过程中,需要提前准备好一些类和方法,这些类和方法的初始化是调用 libs 文件夹下的大多数 php 文件来实现的。libs文件夹下的文件如下:

其中,uoj-lib.php 文件是进行所有相关初始化的主文件,上级的 index.php 文件会直接调用该文件展开初始化的工作。libs 文件夹下的其他文件和 model 文件夹下的所有类都是通过 uoj-lib.php 文件来完成初始化工作的。

controller文件夹

该文件夹是用来存放控制器文件的,也就是构建HTML文件的主要文件。controller文件夹下的文件如下:

其中, judge 文件夹下的文件负责处理OJ的判题逻辑。比如,评判提交的代码是否AC/WA/TLE/MLE等。
这里以当前文件夹下的 index.php 为例来描述主页的生成。

生成主页的 index.php

先给出 index.php 的代码:

可以看到从这里开始php文件中有 HTML 的语句出现。
事实上,controller文件夹下的每个php都对应着一个网页的主要内容的生成。观察 index.php 生成的主页:

index.php 中的所有 HTML 语言的代码都是实现上图中的第2模块部分。

注意到代码:

<?php echoUOJPageHeader(UOJConfig::$data['profile']['oj-name-short']) ?>

此部分通过先前初始化的类和方法调用 /app/views/page_header.php 文件,结合 /app/views/main-nav.php 文件来生成整个网页的header部分。

注意到代码:

<?php echoUOJPageFooter() ?>

此部分通过先前初始化的类和方法调用 /app/views/page_footer.php 文件来生成整个网页的footer部分。

在这样的情况下,可以灵活调用这些php语句,来应对一些特殊网页的生成。

DeerOJ的工作原理

服务端收到请求后,会运行 web 文件夹下的 index.php 文件(由同目录下的.htaccess决定)

index.php 文件的内容截图如下:

index.php 会加载所需的函数库和类库,具体如下:

require $_SERVER['DOCUMENT_ROOT'] . '/app/libs/uoj-lib.php';

该句是调用 /app/libs/ 下的php文件,用来调用一些类和方法。

uoj-lib.php 的完整代码如下:

注意到 uoj-lib.php 下有如下代码段:

function requirePHPLib($name) { // uoj php lib
		require $_SERVER['DOCUMENT_ROOT'].'/app/libs/uoj-'.$name.'-lib.php';
	}
	
	requirePHPLib('validate');
	requirePHPLib('query');
	requirePHPLib('rand');
	requirePHPLib('utility');
	requirePHPLib('security');
	requirePHPLib('contest');
	requirePHPLib('html');

可知在代码中调用了同类文件夹的其他 lib.php 文件,这些文件的具体功能大致如下:

  • validate:验证lib,用来验证某些信息是否合法,比如账号密码等
  • query:查询lib,用来调用后台数据库查询相关信息,结合 model 文件夹下的 DB.php 类使用
  • rand:随机lib,用来生成随机数据(随机数、随机文件名等)
  • utility:utility—lib,用来实现一些在web上的功能,比如配置合并,字符串处理等
  • security:安全lib,用来维护账号的安全,比如密码处理,token等
  • contest:比赛lib,用来处理比赛相关的方法集,比如计算rating,更新人数等
  • html:HTMLlib,用来接收网页相关信息、输出html代码等

注意到 uoj-lib.php 下有如下代码段:

	Session::init();
	UOJTime::init();
	DB::init();
	Auth::init();
    	
	if (isset($_GET['locale'])) {
		UOJLocale::setLocale($_GET['locale']);
	}
	UOJLocale::requireModule('basic');

此段均是对 /app/models/ 下的部分类进行初始化,其中:

  • Session:初始化会话层相关的信息
  • UOJTime:初始化时间信息
  • DB:初始化和数据库的连接
  • Auth:初始化登录用户的信息
  • UOJLocale:中英文相关的配置信息

回到web 文件夹下的 index.php 文件,注意到如下代码:

require UOJContext::documentRoot().'/app/route.php';

其中,UOJContext类是用来管理和获取与当前Web请求相关的上下文信息,例如获取服务器环境信息,blog处理等。该句是调用路由文件。路由文件(route file) 去给请求中的网址匹配用于生成响应报文的 PHP 代码,结合 models 下的 Route 使用。 Route 定义了一个简单的路由系统,允许通过静态方法定义和管理路由。通过 match、get、post 等方法,可以方便地为不同的HTTP请求方法定义路由。通过 group 方法,可以对一组路由应用共同的属性。dispatch 方法用于遍历和匹配请求的路由,并执行相应的操作。整个路由系统利用静态属性和方法来管理路由,使其在应用中便于使用和扩展。

index.php 文件中,注意到如下代码:

include UOJContext::documentRoot().'/app/controllers'.call_user_func(function() {
	$route = Route::dispatch();
	$q_pos = strpos($route['action'], '?');
	
	if ($q_pos === false) {
		$path = $route['action'];
	} else {
		parse_str(substr($route['action'], $q_pos + 1), $vars);
		$_GET += $vars;
		$path = substr($route['action'], 0, $q_pos);
	}
	
	if (isset($route['onload'])) {
		call_user_func($route['onload']);
	}
	
	return $path;
});

这里是动态调用库文件,主要是根据用户的网页请求来调用。比如申请主页,会调用 controllersindex.php 文件。

main-bar 上的标签样式设计

posted @   山城甘草  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示