
简介这是一套基于PHP开发的EnableQ在线问卷调查引擎完整源码包面向需要部署在线问卷系统的PHP开发者、站长及调研人员。资源包内共2263个文件以1317个PHP业务逻辑文件、704个HTML页面模板为核心辅以CSS/JS前端样式与交互、GIF/PNG/JPG图片素材、SQL数据库脚本以及Swf动态组件等压缩包整体仅9.57MB适合快速下载部署。目前已有104人学习下载。解压后可见响应式样式文件如pad.css、phone.css等说明系统对移动端与平板访问做了适配完整目录结构便于二次开发或按模块调用。通过本包可了解在线问卷引擎的UI渲染、数据存储、逻辑跳转、统计分析与发布分享等核心实现为自建问卷平台或学习PHP项目架构提供可直接运行的参考。1. 拿到 EnableQ 之后先知道这是一套什么样的 PHP 问卷引擎运营周三提了个需求下周要对三千个客户发满意度问卷。用第三方问卷平台数据在别人手里导出还要二次处理从零自己写光单选、多选、矩阵、跳题逻辑就能拖半个月。EnableQ 就是这中间出现频率很高的一个选型——一套基于 PHP 的在线问卷调查引擎源码部署在自己的服务器上问卷创建、填答、统计都在同一套包里完成数据自持。这篇东西按拿到源码之后的动手顺序写先讲清楚它的运行方式和数据模型再把最小部署跑通接着讲接入公司登录体系、定制模板和导出答卷最后补几个老工程师才会注意的坑。目标读者是已经能独立配置 PHP 环境、准备在一两天内上线自托管问卷系统的工程师新手可以照着命令走熟手可以重点看参数边界和排障思路。2. EnableQ 的运行原理与数据模型先看懂问卷是怎么被跑起来的拿到一套不熟悉的 PHP 系统第一件事不是看代码而是先把它的请求链路和表结构理顺。EnableQ 这类问卷引擎之所以能维持多年稳定核心原因是它把“问卷配置”“填答过程”“统计结果”三个环节彻底分离了。2.1 填答、管理、统计三条链路的职责边界一个用户在浏览器里打开问卷链接到管理员在后台看到统计图表中间其实经过了三套互相独立的 PHP 流程。第一套是填答端用户通过 survey 链接进入引擎从数据库读出问卷配置按题目顺序渲染表单用户提交后写入答卷记录。第二套是管理端管理员在这里建问卷、维护题目、配置跳题逻辑这些操作只修改问卷配置表不碰答卷数据。第三套是统计端它只做聚合查询从答卷明细表里按题目维度算出分布和占比。这三条链路在代码层面通常是三个独立的入口目录。你在源码包会看到类似survey、admin、stat这样的目录划分。写代码的时候要注意填答端和管理端对 session 的依赖强度完全不同填答端允许匿名访问管理员入口必须做权限校验。如果后续要改登录体系只需要在填答端入口处加一层“识别当前用户是谁”的逻辑而不需要动问卷引擎本身的题目渲染逻辑。2.2 核心表结构问卷配置与答卷数据为什么必须分开存问卷系统最怕的就是把题目定义和用户答案混在一张表里那样每改一次问卷结构就要重建数据。EnableQ 的常见做法是分成四类基础表问卷表、题目表、选项表、答卷表及答卷明细表。问卷表存问卷标题、状态、起止时间题目表存每个题目的类型、题干、排序选项表存单选多选的候选项答卷表存一次完整提交答卷明细表存每一道题的具体答案。下面的建表片段可以帮你快速理解它们的关联关系。CREATE TABLE enq_survey ( sid INT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(200) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0草稿 1发布 2结束, start_time DATETIME, end_time DATETIME, PRIMARY KEY (sid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE enq_question ( qid INT UNSIGNED NOT NULL AUTO_INCREMENT, sid INT UNSIGNED NOT NULL, qtype TINYINT NOT NULL COMMENT 1单选 2多选 3填空 4矩阵, title VARCHAR(500) NOT NULL, sort_order SMALLINT DEFAULT 0, is_required TINYINT DEFAULT 0, PRIMARY KEY (qid), KEY idx_sid (sid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;跨表关联的核心字段是sid和qid所有题目、选项、答卷都通过这两个 ID 串起来。试卷配置表只存“有哪些题”答卷明细表只存“用户选了哪个选项ID”统计端最后通过 JOIN 把题目文本和选项文本关联回来。这样设计的好处是问卷发布后不允许修改题目结构但允许追加答卷统计脚本只需要扫描明细表不用担心题目配置变化影响历史数据。2.3 为什么这种老牌 PHP 项目还能用依赖边界与版本注意点EnableQ 这类系统的代码普遍是面向 PHP 5.x 时代写的但这并不代表它跑不到 PHP 7.4 或 8.0 上。它依赖的通常只有 mysqli、session、json 这几个基础扩展而真正的兼容性风险来自三个地方一是mysql_*函数是否被替换成mysqli_*二是 Smarty 模板引擎的版本是否过老三是字符集连接是否显式声明 utf8mb4。在实际部署之前先确认安装包里的数据库驱动用的是哪套 API再对照 PHP 版本决定是否需要打兼容补丁。对绝大多数场景把 PHP 版本固定在 7.4 是最稳妥的选择既不落后太多又能绕开 8.x 引入的若干破坏性变更。3. 把 EnableQ 部署到 PHP 环境Windows 下的跑通顺序与 Linux 生产配置部署问卷引擎比部署普通网站多一个关键动作初始化数据库。所以整个流程分成三个步骤准备 PHP 运行环境、导入数据库并生成配置文件、验证填答和管理两个入口。先讲本地最快路径再讲生产环境的差异。3.1 本地快速跑通用 PHPStudy 在 Windows 上搭最小环境Windows 上做开发验证PHPStudy 这类集成环境仍然是最省事的做法对应热搜里大量“php安装与配置windows”的需求。下载一个带 Apache Nginx MySQL 的版本把 PHP 版本切到 7.4然后在站点设置里把域名根目录指向 EnableQ 解压后的目录开启伪静态伪静态规则默认在安装包里的nginx.conf或.htaccess中能找到。浏览器访问http://localhost进入安装引导页。安装引导页通常只需要填数据库地址、用户名、密码、数据库名以及一个管理员账号。安装过程会做两件事创建数据表并生成一个配置文件常见文件名是config.inc.php或setting.php。下表是安装时必须确认的参数和建议值。参数建议值说明数据库地址127.0.0.1不推荐写 localhost避免 PHP 走 socket 导致连接失败数据库名enableq不要用中文库名避免字符集问题表前缀enq_如果一台服务器要跑多套问卷用前缀隔离PHP 版本7.4兼容性与安全性的折中点字符集utf8mb4防止生僻字和 emoji 答案写入失败安装完成后立刻做两步验证访问前台问卷页确认模板能渲染访问/admin确认后台能登录。如果前台显示乱码优先检查 MySQL 连接字符集是否设置为utf8mb4而不是急着改文件编码。3.2 Linux 生产部署命令行初始化数据库并调整配置文件生产环境我一般会用命令行手动初始化而不是完全依赖可视化安装向导这样后续迁移和批量部署都容易复制。先建库、导入安装包自带的 SQL 文件再手动生成配置文件。mysql -uroot -p -e CREATE DATABASE enableq DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; mysql -uroot -p enableq enableq.sql cp config.sample.php config.inc.php chmod 640 config.inc.php安装包内通常自带一个enableq.sql它比安装向导生成的表结构更完整。手动导入后编辑config.inc.php填入数据库连接参数。chmod 640的目的是让 PHP-FPM 进程可读、但同服务器上的其他用户不可读避免配置泄露。接下来把站点根目录的data目录存放上传和缓存设为可写chown -R www-data:www-data data。这段命令需要理解两个细节导入 SQL 时库名必须与配置文件里的库名保持一致否则后台能打开但所有问卷列表为空配置文件里的db_prefix项如果改了必须确认 SQL 文件中的表前缀与之相同否则会出现“找不到表”的白屏错误。生产环境还建议在 Nginx 里禁止访问.sql和config.inc.php这类敏感文件典型写法是拒绝~* \.sql$和config\.inc\.php。3.3 安装失败的三个高发原因版本、扩展与目录权限部署季最常见的报错有几种按照出现频率排序。一是 PHP 版本过高导致mysql_connect()未定义解决办法是把 PHP 切换到 7.4或者找到代码里调用旧函数的位置做一次全局替换。二是缺少fileinfo或curl扩展问卷引擎在导入导出功能里会用到报错信息通常直接给出函数名按提示启用对应扩展即可。三是data目录不可写症状是问卷发布后保存失败或者验证码不显示。php -m | grep -E mysqli|curl|fileinfo在启动服务之前跑一下上面的命令确认三个扩展都在。如果fileinfo缺失Debian/Ubuntu 上执行sudo apt install php7.4-fileinfo就能解决。目录权限类问题则用ps aux | grep php-fpm确认运行用户之后再决定chown给谁不要直接chmod 777整个站点目录否则后患无穷。4. EnableQ 二次开发实战登录打通、模板定制与答卷导出部署完成只是开始实际使用中最常见的三个二次开发需求是让问卷知道填答人是谁、把页面改成自己公司的样式、把答案数据导出到 Excel。这三件事都不需要动问卷引擎的核心逻辑而是在它的扩展点做文章。4.1 从匿名问卷到实名问卷在提交入口注入用户身份现在的问卷系统至少要回答一个问题这份答卷是谁填的。EnableQ 默认是匿名填答但它的设计保留了一个可用的扩展点——在用户打开问卷时把身份信息注入 session然后提交时随表单一起写入答卷明细。常见做法是找到问卷前台的入口控制器在生成问卷页之前先读取当前登录用户。session_start(); if (!isset($_SESSION[staff_id])) { header(Location: /login.php?redirect . urlencode($_SERVER[REQUEST_URI])); exit; } $tpl-assign(staff_name, $_SESSION[staff_name]); $tpl-assign(staff_department, $_SESSION[staff_dept]);这段逻辑放在模板渲染之前作用是强制登录校验并把用户信息赋值给模板引擎。Enqire 的模板层支持变量输出你只需要在问卷 HTML 里input typehidden nameext_staff_name value{$staff_name}提交后接收端就能拿到这个字段。注意不要用$_SESSION直接拼进 SQL而是通过参数绑定写入。业务上建议把staff_id作为唯一标识参与查重部门、姓名只做展示。如果公司已有统一登录接口替换这里的$_SESSION赋值代码即可后端存储逻辑完全不用改。4.2 页面模板定制找到模板文件与数据变量的对应关系EnableQ 的页面渲染采用了模板引擎方案源码解压后能看到templates目录里面都是.tpl文件这意味着改页面样式不需要动 PHP 逻辑。模板目录里的目录层级通常和前端页面一一对应header.tpl控制页头footer.tpl控制页脚question_radio.tpl控制单选题的 HTML 结构。下面这段示例展示的是在控制器中给模板赋值让每个页面都能拿到站点名称和自定义 CSS 路径。$tpl-assign(site_name, 客户体验中心); $tpl-assign(site_css, /assets/custom.css); $tpl-display(header.tpl);这种“控制器赋值、模板展示”的架构对二次开发很友好。需要维护两套问卷模板时可以直接复制整个templates/default目录为templates/winter然后在配置里切换默认模板目录互不影响。如果模板文件中出现{$tpl_var|escape}这类写法那是模板引擎的转义语法不要手动删除escape过滤器否则用户输入的特殊字符会被直接输出造成存储型 XSS 风险。另外返回给前端的接口数据建议统一用array组装后json_encode输出方便前端算数据也方便排查字符编码。4.3 把答卷导出为 Excel用一条 PHP 脚本搞定 CSV 输出后台自带的导出功能通常只导出原始文本遇到多选题答案挤在一个单元格里的情况二次处理成本很高。更可控的办法是写一个专用导出脚本通过问卷 ID 拉取明细数据然后用fputcsv输出 CSV 文件。CSV 的好处是 Excel 和 WPS 都能直接打开不需要引入沉重的 PHPExcel 依赖。$fp fopen(php://output, w); fwrite($fp, \xEF\xBB\xBF); // 添加 UTF-8 BOM防止 Excel 打开乱码 fputcsv($fp, [工号, 部门, 题目, 答案]); $sql SELECT q.title, d.answer_text FROM enq_answer_detail d LEFT JOIN enq_question q ON d.qid q.qid WHERE d.sid ? ORDER BY d.aid; $stmt $db-prepare($sql); $stmt-execute([$_GET[sid]]); while ($row $stmt-fetch(PDO::FETCH_ASSOC)) { fputcsv($fp, [$staff_id, $dept, $row[title], $row[answer_text]]); } fclose($fp);这段代码的关键在于\xEF\xBB\xBF这 3 个字节。UTF-8 编码的 CSV 文件如果不带 BOMExcel 会默认按 GBK 解析导致中文全部变成乱码。php://output直接输出流配合 Nginx 返回application/octet-stream头用户点击就能下载。数据量大时要分页读取不要一次性fetchAll三千份答卷用流式输出几乎不占内存。导出字段按业务需要调整把多选题的answer_text按分隔符拆成多行也是一种常见做法。5. 把 EnableQ 当“问卷中台”的进阶技巧队列落库、上传防护与调试验证接手任何 PHP 项目都要做三件事防止重复提交、堵住上传漏洞、留下可观测的日志。放到 EnableQ 场景里也是这样尤其问卷这种高并发、短时突增比如全员同时填答的业务形态更需要提前设防。5.1 高并发下答卷写入的常见瓶颈用 Redis 队列削峰三千人同时提交问卷时MySQL 的写入压力会瞬间升高。常见做法是提交接口先写 Redis 队列再由 CLI 脚本批量消费对应搜索热词里的“php redis 消费组”。代码层面只需要在提交处理里把原来的insert换成LPUSH后台用定时任务消费。$redis-lpush(survey:answer_queue, json_encode($answerData, JSON_UNESCAPED_UNICODE));CLI 脚本使用BRPOP阻塞读取队列拿到数据后批量写入数据库。这样做的好处是前端提交响应时间降到毫秒级数据库压力也被摊平。队列消费脚本要加一个去重逻辑以staff_id sid为唯一键消费前先查一次答卷主表防止网络重试导致的重复记录。如果 Redis 里堆积量持续增长优先检查 PHP 进程的timeout设置和 MySQL 慢查询日志而不是盲目增加消费者数量。5.2 文件上传的安全底线扩展名白名单与目录禁用执行问卷系统经常需要用户上传图片附件和附件材料这是 PHP 应用被植入后门的高发区域历史上大量真实漏洞都是从“一句话木马 php 文件上传”演化出来的。EnableQ 如果开了附件功能务必遵守两条底线上传目录禁止执行 PHP文件类型做白名单校验。location ~* /data/uploads/.*\.(php|php5|phtml)$ { deny all; }Nginx 里把上传目录的 PHP 解析关掉关键是不让data/uploads下的文件命中fastcgi_pass。同时在后端代码里校验扩展名和 MIME 类型不要相信用户传的Content-Type也不要只靠前端 JS 校验。比较稳妥的做法是保存文件时强制改名为随机字符串并去掉原始文件名中的特殊字符从根上杜绝.php文件进入可执行目录。切记关闭安装目录里的测试脚本部署完毕立即删除install目录否则攻击者可以通过重放安装流程直接覆盖数据库配置。5.3 排错技巧用 Xdebug 和日志验证一份答卷的完整流向问卷答完了数据也在库里但统计报表数字对不上这种问题最让人头疼。排查思路是从入口到落库逐段验证不要盯着统计报表猜。先在submit.php入口加一行error_log输出取到的$_POST数组和生成的答卷 ID再在 Nginx access log 里过滤出这个答卷 ID确认请求到达了哪台机器。如果用了 Redis 队列还要确认消费脚本是否执行成功。本地开发可以用 Xdebug 打断点跟踪一次提交请求IDE 里观察$_SESSION和提交数据的变化过程这一步对理解 EnableQ 的填答生命周期帮助很大。热词里“idea php debug”对应的就是这种场景CLI 模式下直接用 PHP 内置服务器跑一个测试脚本把 Xdebug 的xdebug.modedebug打开IDE 里监听 9003 端口就能看到每行代码的实际变量值。线上环境不要开 Xdebug靠error_log和慢查询日志足够定位大多数问题。最后说一个性价比很高的验证技巧发布问卷后先填一份测试答卷然后直接查enq_answer_detail表里是否能同时找到sid、qid、answer_text三个字段的内容如果答案文本有值但题目对不上优先检查跳题逻辑的配置而非代码。本文还有配套的精品资源点击获取