一个用原生 PHP 写的编程学习站:10 章课程 + 页面里直接写、直接跑的代码编辑器 + 练习场 + 测验 + 学习进度。
没有 Composer、没有 npm、没有 MySQL。仓库里带了一份静态 PHP 8.3 运行时,拿到手不用装任何东西,跑 ./start.sh 就能用。
课程(10 章 16 课)
从 echo "Hello, World!" 起步:变量与运算符 → 流程控制与循环 → 数组 → 函数 → 字符串 → 表单与安全 → 面向对象 → PDO 数据库 → 异常与调试 → 最后用一个 Todo 项目把前面串起来。
每课包含:讲义、1~3 个可复制示例、「本课要点」、课后练习(折叠式提示 + 参考答案)。
在线代码编辑器
课时页和练习场里都嵌了一个编辑器,当课的示例会变成上方的「示例 N」标签,点一下直接载入:
- 改完点「运行代码」或按
Ctrl/⌘ + Enter,下面直接显示运行结果和报错 - 报错旁边会补一句人话提示,比如少写分号时告诉你「先看报错的行号」
- 死循环会被 5 秒掐掉,不会把服务卡住
Tab按 4 个空格缩进,代码框会随内容长高,「重置」可还原
代码是真的送到本机 PHP 进程里执行的(不是前端装样子),限制见下面「运行代码的安全说明」。
练习场:5 道独立练习题 —— 温度转换、FizzBuzz、数组去重排序、字符频率统计、写一个计算器类,每道都能当场写、当场运行。
测验:3 套选择题,交卷立刻判分,错题标出来并给解析,成绩入库。
进度:「标记完成」记录课时进度,进度页有总进度、分章进度条和测验历史。
cd php-tutorial
./start.sh脚本会依次做:找 PHP(优先用自带的 runtime/php,其次系统 PHP,都没有就联网下一份)→ 检查 sqlite3 / pdo_sqlite / session 扩展 → 从 8090 开始挑一个空闲端口 → 启动服务 → 打开浏览器。
| 命令 | 作用 |
|---|---|
./start.sh |
启动并打开浏览器(终端里 Ctrl+C 停止) |
./start.sh stop |
停掉正在运行的服务 |
SKIP_BROWSER=1 ./start.sh |
只启动服务,不开浏览器 |
./start.sh --help |
查看用法 |
已经启动过的话,再执行会认出旧实例并直接打开浏览器,不会重复起进程。Windows 下请用 Git Bash 或 WSL。
不想用脚本,自己机器上有 PHP ≥ 8.0(含 sqlite3、pdo_sqlite)也行:
php -S localhost:8080首次访问会自动在 data/ 下建 SQLite 数据库,不需要任何配置。
服务只监听本机 127.0.0.1,代码也只在你自己的电脑上执行。执行学生代码时套了几层限制:
open_basedir锁在data/sandbox/,只能读写自己的临时文件;临时文件跑完立刻删除- 时间上限 5 秒、内存上限 64M(都可以在
config/config.php里调) shell_exec/exec/system/proc_open/curl_*/socket_*等函数被禁用- 单次提交代码最长 20000 字节,输出过长会自动截断
所以在站里跑不了 shell,也发不出网络请求。如果课程正好讲到这些内容,就在自己电脑的命令行里动手试。
php-tutorial/
├── start.sh # 一键启动(找 PHP / 选端口 / 开浏览器)
├── router.php # 内置服务器路由脚本(目录防护 + 旧地址跳转)
├── index.php # 入口:页面路由 + JSON 接口
├── runtime/php # 自带的静态 PHP 8.3 运行时(离线可用)
├── config/config.php # 配置:数据库路径、在线运行的开关与限制
├── core/
│ ├── functions.php # 建库、进度、判分等公共函数
│ └── runner.php # 在线代码运行器(沙箱、超时、报错翻译)
├── data/
│ ├── courses.php # 全部课程内容(10 章 16 课 + 练习题)
│ ├── quizzes.php # 测验题库与判分逻辑
│ ├── database.sqlite # 首次访问自动生成
│ └── sandbox/ # 在线运行的临时目录(自动创建、自动清理)
├── templates/
│ ├── header.php / footer.php
│ ├── home.php # 首页与课程大纲
│ ├── course.php # 章节列表 / 单章详情
│ ├── lesson.php # 课时页(讲义 + 示例 + 在线编辑器 + 练习)
│ ├── exercise.php # 练习场
│ ├── quiz.php # 测验页
│ ├── progress.php # 学习进度页
│ └── code_editor.php # 在线代码编辑器组件(课时页与练习场共用)
└── assets/
├── css/style.css # 全站样式(响应式,无框架)
└── js/app.js # 复制代码 / 标记完成 / 测验 / 在线运行
两张表,首次访问时自动创建:
user_progress—— 课时完成记录(按 session 区分)quiz_records—— 测验成绩与答案快照
- 不用框架、不用第三方库,就是想让每一段代码都能被从头读到尾
array_find()/array_find_key()写了 polyfill,PHP 8.0 ~ 8.4 都能跑- 所有输出都过
htmlspecialchars();进度与测验走POST + JSON,前端拿结果自己渲染 - 课程内容全集中在
data/courses.php,加课只动这一个文件 router.php拦两类请求:data/、core/、config/等目录不允许直接访问;URL 里带了/php-tutorial/前缀(比如从旧地址点进来)会 301 跳回正确地址
编辑 data/courses.php,照着现有结构追加一个章节:
[
'id' => 11,
'title' => '第十一章:Composer 与自动加载',
'icon' => '📦',
'description' => '……',
'lessons' => [
[
'id' => 1,
'title' => '11.1 ……',
'duration' => '15分钟',
'content' => '<p>HTML 内容……</p>',
'code_examples' => [
['title' => '示例标题', 'lang' => 'php', 'code' => '<?php ...'],
],
'key_points' => ['要点1', '要点2'],
'practice' => ['question' => '…', 'hint' => '…', 'answer' => '<?php …'],
],
],
],加进去之后,课时页会自动多出对应的「示例 N」标签,练习场的入口也在同一套结构里,不用改模板。