简介:PHPCMS V9 UTF8 是一套基于 PHP 与 MySQL 的内容管理系统安装包,面向需要搭建多语言网站、资讯门户或企业站的技术人员与站长。包体约 8.39MB,压缩包以 zip 格式分发;上传信息暂缺文件总数与内部类型明细,建议下载后先检查目录结构再依据模块逐步使用。该版本围绕 UTF8 编码设计,能较好支持中文、英文等多语言页面,并内置内容发布、分类管理、模板引擎、权限控制、模块化扩展、会员系统及广告管理等功能,涵盖新闻、图片、下载、视频等常见内容模块,覆盖了 CMS 部署与运维的常见环节。已有 277 人学习下载,适合 PHP 开发者、网站管理员从入门到进阶参考;通过安装与配置这套系统,可以加深对 CMS 架构、数据库关联和前端模板机制的理解,为后续二次开发和站点运营打下基础。
1. 老站维护与新站搭建都在问:PHPCMS V9 UTF8 到底还能不能打
作为在 2010 到 2016 年间被大量中小站点采用的 PHP 内容管理系统,PHPCMS V9 到今天最值钱的资产不是框架本身,而是海量现成的模板与插件积累。UTF8 版本意味着数据库、程序文件、页面输出统一在 UTF-8 编码之下,中文站、资讯站、多语言站选它都能少一层编码折腾。我这几年接手的旧站迁移项目里,有两家原本是 GBK 版本,数据乱到不敢动,最后都绕回 PHPCMS V9 UTF8 重做。这篇文章从环境选型、数据库字符集、二次开发写到备份迁移的坑,新手能照做,熟手可以直接跳到排查章节对号入座。
2. 先搭对地基:PHP 环境、MySQL 字符集与 PHPCMS V9 UTF8 安装的三个关键项
2.1 环境选型:PHP 5.6 与 MySQL 5.7 是最不折腾的组合
PHPCMS V9 的核心代码诞生于 PHP 5.2、5.3 时代,大量直接调用 mysql_* 系列函数。PHP 7 在 2016 年彻底移除了这些函数,直接拿新环境安装 V9 大概率连安装向导都过不去,报一个 “Fatal error: Unsupported operand types” 或者 “Call to undefined function mysql_connect()”。
我一般会给客户两条路:站点规模不大、只做内容维护,直接上 PHP 5.6 + MySQL 5.7,这是兼容性最省心的一组。如果公司运维规范强制 PHP 7+,那就需要给 V9 打一套 mysqli 兼容层补丁,本质是把 mysql_query、mysql_fetch_array 这些调用统一替换成 mysqli 版本,再处理掉 PHP 7 下字符串和数组访问方式的差异。这套方案能用,但每次升级框架版本都要重新过一遍,不建议新手尝试。
数据库选型,MySQL 5.7 是对 UTF-8 支持最稳定的版本,V9 安装时建的默认表结构全是 MYISAM 引擎、utf8_general_ci 排序规则,5.7 里跑得很快。MariaDB 10.2 之后的版本跑 V9 也没有问题,但如果客户用老的主机面板,PHP 和 MySQL 版本往往由面板锁定,装之前先确认 mysqli、gd、curl、zlib、mbstring 五个扩展都在。mbstring 这个很多人忽略,V9 的字符串截断函数和关键词分词都依赖它,缺少的话后台文章列表会直接报错。确认方式最简单的是在站点根目录临时放一个 phpinfo 文件,浏览器打开后搜 mbstring、mysqli、gd 三个关键字。
php -m | grep -E 'mysqli|mbstring|gd|curl|zlib'逻辑说明:这条命令直接在命令行列出当前 PHP 已加载的模块,grep 筛选出 V9 必需的那几个。线上环境如果只给你面板没有命令行,就退回到 phpinfo 页面肉眼核对。五种扩展缺哪个装哪个,别急着往下走,V9 的环境检测页面也会给提示,但它的检测逻辑只拦必选项,mbstring 这类软依赖往往放行,后面跑到分词和截断才爆。
2.2 安装向导中必须改的三个默认项
把源码包解压上传后,访问 http://你的域名/install 进入安装向导。页面会先做环境检测,重点看目录权限一栏,phpcms/caches、uploadfile、statics 三个目录要有写入权限,否则后续生成缓存和数据上传都会失败,这里翻车最频繁。
传统 LNMP 或面板环境下的权限处理,我通常这么给:
chown -R www:www /var/www/html/phpcms chmod -R 755 /var/www/html/phpcms chmod -R 777 /var/www/html/phpcms/phpcms/caches chmod -R 777 /var/www/html/phpcms/uploadfile参数说明:chown -R 把站点目录递归归属到 Web 服务运行用户,这是避免 PHP 写文件时报 Permission denied 的前提。caches 和 uploadfile 给 777 是 V9 官方安装文档的常见做法,生产环境如果安全要求高,可以把这两项收紧到 755 加属主写权限,但新手不要擅自改,否则缓存目录写入失败后台会一直转圈。
安装表单里我给客户必改三个默认项。第一,数据库字符集选项,界面显示的是“数据库字符集:utf8 / gbk / utf8mb4”,选 utf8 而不是 gbk,这套 UTF8 版本程序内部所有 SQL 都按 utf8 构造,选 gbk 会导致后台录入中文正常、前台读取乱码的割裂问题。第二,数据库表前缀默认 v9_ 不要动,后续很多二次开发插件硬编码了这个前缀,改了之后再装插件会找不到表。第三,后台管理员账号,V9 默认的管理员账号是 admin,密码要求至少 6 位,这里要立刻换成不包含用户名片段的独立密码,安装完成后管理员入口是 /admin.php。
安装最后一步会有一行“是否生成演示数据”,我通常建议不勾选。演示数据会带一批旧栏目和文章,后面删起来很费劲,而且演示数据里包含的部分图片路径不符合 UTF8 版本的标准,留着容易让客户误以为站点出了问题。安装结束后,立刻把 install 目录整个删掉,这不是洁癖,V9 的 install 目录在删除前可以被直接访问,存在被重装覆盖数据库的隐患。
2.3 数据库端字符集核对:utf8 与 utf8mb4 的取舍
安装完成后第一件事不是进后台配栏目,而是核对数据库的真实字符集。PHPCMS V9 UTF8 安装时会在连接层把默认字符集设置成 utf8,但服务器的 my.cnf 或面板的 MySQL 配置可能把全局 character_set_server 设成了 latin1 或 gbk,这种环境下建出来的表虽然名为 utf8,实际排序会出错。
登录 MySQL 看一眼当前环境:
SHOW VARIABLES LIKE 'character\_set\_%'; SHOW VARIABLES LIKE 'collation\_%';我这几年见过的异常情况里,character_set_server 直接决定新建数据库的默认字符集,如果这里不是 utf8,进后台创建栏目、批量录入文章时,中文内容会在数据库层面被转成问号,程序本身没有任何报错,属于典型的黑匣子问题。处理办法是修改 my.cnf 的 [mysqld] 段加上 character-set-server=utf8,然后重启数据库。已经建好的库可以用 ALTER DATABASE 修正:
ALTER DATABASE phpcms_v9 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;逻辑说明:ALTER DATABASE 只改库的默认字符集,不会自动转换表结构,所以它适合安装初期表还没建完或者表结构本身正确的情况。如果你已经录入了大量数据,需要连同表和列一起转换,这就移到后面备份恢复章节讲。关于 utf8mb4,MySQL 5.5 之后才支持,utf8 最多存 3 字节,遇到 emoji 或生僻字就直接失败。V9 的 UTF8 版本官方只支持到 utf8,装完默认表里用 utf8mb4 也能读,但后续建表和字段长度匹配经常出问题。我的建议是:站点没有表情符号和特殊字符需求,就老老实实用 utf8;有这类需求,按第六章的方式在程序不变的前提下做一次库和表的字符集改造,不要直接在安装向导里选 utf8mb4,V9 的安装脚本对 utf8mb4 的兼容并不完整,选了之后部分建表语句的字段长度会溢出。
| 检查项 | 期望值 | 异常时的典型表现 |
|---|---|---|
| PHP 版本 | 5.6 | 安装向导直接白屏或 Fatal error |
| mbstring 扩展 | 已启用 | 后台文章列表、标题截断异常 |
| character_set_server | utf8 | 中文内容存成问号 |
| 表前缀 | v9_ | 插件找不到数据表 |
| install 目录 | 已删除 | 可被再次访问重装 |
3. 三层字符集一致性:文件编码、数据库连接与页面声明的联动
V9 UTF8 版本整条链路上,程序文件、配置文件、模板、数据库表结构、HTTP 响应头这五个环节必须保持同一套编码逻辑。日常维护最常遇到的乱码,几乎全是其中一环被改成了 GBK 或者加了 BOM。
3.1 文件编码:UTF-8 无 BOM 为什么是硬性要求
V9 源码包里的 PHP 文件都是 UTF-8 无 BOM 编码。BOM 是 UTF-8 文件开头的三个不可见字节 EF BB BF,PHP 引擎读到这仨字节会把它们当作输出内容直接发送到页面顶部,表现就是所有页面顶部多一个空行,后台有数据缓存时尤其明显。更烦人的是,如果你用某些编辑器“另存为 UTF-8”,默认往往带着 BOM,一个目录几十个文件同步下来,整个站的模板输出全被顶下来了。
检查整个站点是否混入了 BOM,用 grep 一次搜完:
grep -rl $'\xEF\xBB\xBF' . --include='*.php' --include='*.html' --include='*.js' --include='*.css'参数说明:-r 递归查找目录,-l 只输出包含匹配的文件名,--include 限定文件类型;注意这里用的是 $'\xEF\xBB\xBF' 这个 bash 语法,表示匹配字节序列而不是文本。如果结果有输出,用 iconv 转码或者编辑器批量去掉 BOM。我习惯的做法是直接改编辑器配置,所有新文件默认 UTF-8 无 BOM,从源头杜绝。
另外,不要用 Windows 记事本去编辑 V9 的模板文件。记事本保存的 UTF-8 文件大概率带 BOM,而且它对编解码错误的提示完全不可见,改一个模板保存后整站顶部多空行,排查过程非常折腾。换用支持编码显示的现代编辑器,保存时明确选择“UTF-8 without BOM”,老项目维护这点能省掉一半的玄学问题。
3.2 config.inc.php 中的 charset 参数与数据库连接
PHP 文件字符集、数据库字符集、HTTP 声明字符集三个变量,在 V9 里分别由不同位置控制。核心配置文件 phpcms/caches/configs/config.inc.php 里有两个键值:
<?php return array( 'charset' => 'utf-8', // 页面与模板的输出字符集 'dbcharset' => 'utf8', // 数据库连接的字符集 'db_host' => 'localhost', 'db_user' => 'root', 'db_pwd' => 'your_password', 'db_name' => 'phpcms_v9', 'db_tablepre' => 'v9_', 'db_master' => '0', );这里的 dbcharset 决定了实例化数据库连接后 mysqli 或 PDO 的 SET NAMES 语句。有些从 GBK 版本升级过来的站点,配置文件里 dbcharset 还残留着 gbk,但文件和数据库都换成 UTF-8,结果是后台数据显示正常、前台模板变量输出乱码,因为连接层把 UTF-8 数据按 GBK 解析,写入读出往返一次就全乱了。
改完这个文件,记得进后台“设置-更新缓存”。V9 对配置文件的缓存粒度非常细,只修改文件不更新缓存,前台读到的还是旧值,这个动作忘记做会导致改完后页面完全不动,看起来像没生效。缓存更新的入口在后台右上角的“更新缓存”,点一下全站缓存重建,耗时几秒到几十秒,视站点体量而定。
3.3 模板 header 里的 charset 声明与模板变量输出
V9 模板体系里,页面的编码声明通常在模板的 header.html 里:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />同时 PHP 侧 header 函数输出的 Content-Type 也可能覆盖这个 meta。常见情况是浏览器地址栏直接输入站点首页,页面能根据 meta 正确识别为 UTF-8;但如果程序里 headers 位置不对,HTTP 响应头先输出了 text/html; charset=gbk,meta 标签就失效了,浏览器优先采用响应头编码渲染,随后整个页面变成乱码。排查手段是看浏览器开发者工具里的 Response Headers,确认 Content-Type 字段的 charset 值。
多语言混排场景里,模板中英文混排时 UTF-8 下英文不受影响,但中文字符来自数据库字段或语言包两种途径。V9 的语言包文件在 phpcms/languages/ 目录,每个模块一个 zh-cn.php,这个文件如果被改成了其他编码,就会导致后台界面一部分中文正常、一部分乱码。检查语言包编码和 config.inc.php 的 charset 是否一致,是这类疑难乱码的首选突破口。
标题截断也是一个高发点,模板里常见的写法是:
{php echo str_cut($r['title'], 30);}str_cut 是 V9 内置的字符串截断函数,底层走 mbstring 扩展,字符集参数取自 config.inc.php 的 charset。如果服务器缺少 mbstring,这个函数会退回 strlen 逻辑,中文按字节数截断,30 个字符的中文标题显示成 10 个字加省略号,看起来是数据有问题,实际上是扩展缺失。遇到标题截断异常,先检查 phpinfo 的 mbstring 是否启用,再去看数据表。
4. 在 PHPCMS V9 UTF8 上做二次开发:模板标签、数据模型与自定义模块
二次开发是 V9 老站维护绕不开的环节。很多客户拿着当年外包做的模板,改需求时要把列表、分页、内容页全部重做,这套标签语法搞懂后能复用的地方非常多。
4.1 模板标签:列表、分页与内容调用的最小可复现组合
V9 的模板语言以 {pc:} 和 {loop} 为核心。做一个栏目列表页的最小可运行片段:
{pc:content action="lists" catid="5" num="10" order="listorder DESC" page="$_GET[page]"} {loop $data $r} <div class="news-item"> <h3><a href="{$r[url]}">{$r[title]}</a></h3> <p>{php echo str_cut(strip_tags($r['description']), 100);}</p> <span>{date('Y-m-d', $r[inputtime])}</span> </div> {/loop} <div class="page">{$pages}</div> {/pc}逻辑说明:pc 标签是 V9 内容模型对外暴露的数据查询入口,action 表示调用的动作模型,lists 是列表查询;catid 为栏目 ID,只能传整数;num 是显示条数上限,order 控制排序。loop 是 V9 的模板循环语法,$r 是单条记录的数组,{$r[title]} 在 HTML 中输出不需要加引号,date 函数同样可以直接写在模板里。
参数说明里最容易被改坏的是 order 的值。V9 常见排序字段有 listorder(手动排序)、id(发布时间倒序)、inputtime(录入时间)、updatetime(更新时间)。列表要按最新文章排,应该 order="id DESC" 而不是 inputtime DESC,后者的发布时间在内容修改时不更新,会造成置顶文章不在首位的问题。分页参数 page="$_GET[page]" 是固定写法,V9 的分页组件会读取 $_GET 中的 page 值。如果模板里漏了 page 参数,分页链接点击后永远回到第一页,这是模板二开时几乎碰过一遍的经典错误。
4.2 新增字段与数据模型时的编码陷阱
后台“内容-模型管理”里可以新增字段,比如给文章模型加一个“作者简介”。这里有个硬性规则:字段名只能用英文字母和下划线,且 V9 建表时统一使用默认字符集。对 UTF8 版本来说,新增字段的默认类型是 VARCHAR(255),MySQL 5.6 前单行所有 VARCHAR 字段的字节长度总计不能超过 65535 字节。如果往一个已经有多个 VARCHAR 字段的内容模型里加两个 255 字段,建表会直接报 “Row size too large”,这是我在给某客户加扩展字段时踩过的实坑。
解决思路是把超长文本字段改用 TEXT 类型。V9 后台“字段类型”里选 TEXT,实际建表语句是 TEXT,该字段不占用 varchar 长度配额,能解决溢出报错。另一个更隐蔽的坑是:新增字段后,模板里调用 {$r[字段名]} 输出的值,后台录入正常、前台却是空串,原因通常是后台缓存未更新。新增字段后必须去“设置-更新缓存”,重点更新模型缓存和数据表结构缓存,否则前台查询结果里的字段解析走的是旧缓存,拿不到新增列。这个顺序做反了会觉得改了没反应,实际上是缓存挡住了。
4.3 自定义模块开发:入口文件、数据操作与模块安装脚本
如果业务需要做一个完全独立的模块,比如调查问卷或者预约登记,V9 的扩展方式是开发一个 module,目录结构固定在 phpcms/modules/你的模块名/,包含 index.php 做入口,classes/ 放业务类,templates/ 放模板。
一个最简入口文件写法:
<?php defined('IN_PHPCMS') or exit('Access Denied'); class index { public function __construct() { $this->db = pc_base::load_model('your_model'); } public function init() { $list = $this->db->select(); include template('yourmodule', 'list'); } }逻辑说明:第一行 defined('IN_PHPCMS') 是 V9 的安全守卫,任何模块文件被直接访问时无法通过全局常量检查,直接终止。pc_base::load_model 加载的是 phpcms/model/ 下的数据模型文件,模型文件里继承 base_model 并配置表名即可。include template 调用该模块下 templates/ 里的模板文件,模板变量通过 extract 方式导入模板作用域。
开发模块时文件编码必须与全局一致,UTF-8 无 BOM。如果某个模块文件是 GBK 编码,入口文件被 include 时页面会从该文件位置开始输出乱码,由于 V9 的 URL 路由经过多次 include,这种乱码可能只出现在部分页面,排查起来非常血泪:把 phpcms/modules 下所有文件重新扫一遍编码,是最快的定位方式。脚本里如果有 SQL 语句,建议统一放到 install 目录下的模块安装 SQL 文件中,沿用 utf8 编码,不要直接在 PHP 里写天然带编码风险的 SQL 拼接。
5. 乱码、备份与迁移:PHPCMS V9 UTF8 的常见问题排查
这章是踩坑记录,每条按现象到原因到解决的顺序写,对照你自己的情况入座。
5.1 现象:页面顶部多空行或者整页乱码,后台正常
原因:源文件编码被改动或者 BOM 污染。具体是安装时用 Windows 编辑器批量替换过模板内容,保存成带 BOM 的 UTF-8,所有输出前被注入了 BOM 字节。
解决:用 grep 全站扫描 BOM 文件,对命中的文件批量去 BOM。命令:
find . -type f \( -name '*.php' -o -name '*.html' -o -name '*.js' -o -name '*.css' \) -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;逻辑说明:sed 命令作用是把每个文件第一行的 BOM 三字节替换为空。参数 -i 是原地修改,1s 表示只处理第一行,^\xEF\xBB\xBF 匹配文件开头的 BOM。执行前先备份目录,sed -i 如果遇到超大文件可能截断,这个命令只建议在确认编码问题后使用,不确定时先 grep 出名单再逐个处理。
5.2 现象:备份的 SQL 导入新库后中文全部变成问号
原因:mysqldump 默认导出的 SQL 不含字符集声明,或者备份时数据库连接用了 latin1。导入时执行 SET NAMES 的字符集与文件实际编码不一致。
解决:备份和恢复都显式指定字符集。
mysqldump -h host -u user -p --default-character-set=utf8 phpcms_v9 > v9_backup.sql mysql -h host -u user -p --default-character-set=utf8 phpcms_v9 < v9_backup.sql参数说明:--default-character-set=utf8 让 mysqldump 在导出时和导出文件头部写入相同声明,导入端同样指定,两端一致的字符集才能保证中文不丢。另一个必做动作是恢复前在 MySQL 客户端执行 SET NAMES utf8,让当前连接保持在 utf8。如果你之前是 GBK 库导出的备份,导入 UTF8 库前必须先把备份文件整体转成 UTF-8,否则转换后内容错位,这种历史数据迁移我一般要额外做一轮内容抽查,不能只看条数对。
5.3 现象:搜索中文关键词返回空结果,英文正常
原因:V9 自带的搜索依赖分词函数和全文索引。UTF-8 版本的搜索模块对中文分词依赖 mbstring,服务器缺 mbstring 时中文分词直接失败,查询被过滤为空。
解决:检查 phpinfo 的 mbstring 状态,缺失时启用扩展。另外清理 V9 的搜索缓存表 v9_search_keyword,这个表里的关键词缓存可能被 GBK 数据污染,清理后重新录入即可。这个翻车现场我至少见过三次,每次都是先查数据库后查扩展,最后才发现是 mbstring 没开。搜索模块本身是 V9 相对独立的一块,如果只是少数词查不到,优先看分词缓存表里的词条是不是乱码。
5.4 现象:栏目页正常,文章内容页某些字符变成\u形式乱码
原因:内容编辑器粘贴了 Word 或网页的弯引号、特殊空格,这些字符在保存时被编辑器转成 HTML 实体,数据库里存的是实体字符串,模板输出时又经过一轮转义,导致浏览器显示乱码。
解决:后台系统设置里关闭“自动转义”,或者内容录入时使用纯文本粘贴。数据库层面可以批量修复:
UPDATE v9_news SET content = REPLACE(content, '\u2018', '"') WHERE content LIKE '%u2018%';这个方案要按实际污染字符逐个处理,工作量不小,但它是老站内容清洗的最后一环。注意 V9 用的是 MySQL 表,REPLACE 函数不会触发索引,数据量大时建议分批执行,一次性 UPDATE 几十万行会把表锁住,前台访问直接卡死。
5.5 现象:安装完后台能登录,但所有列表页报“函数不存在”
原因:PHP 版本高于 V9 支持范围,部分函数如 split、each 在 PHP 7 中移除,模块代码里未做兼容替换。
解决:确认 PHP 切换到 5.6;如果必须使用 PHP 7,找到 phpcms/libs 下的函数兼容文件,用向前兼容方式补上废弃函数。这个属于环境兼容问题,不属于编码问题,但排查顺序上它经常先于乱码出现,所以归到这一章一并提醒。
6. 一个藏在配置里的实用技巧:给 PHPCMS V9 UTF8 平滑升级到 utf8mb4
如果客户坚持要在文章里发 emoji 或特殊符号,不改造站点就等着录入时报错。好消息是 V9 UTF8 版本的程序不用改代码,只要动连接配置和表结构,就能平滑升级到 utf8mb4。
第一步,改 config.inc.php 里的数据库连接字符集:
'dbcharset' => 'utf8mb4',第二步,把数据库和内容相关表转换字符集:
ALTER DATABASE phpcms_v9 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER TABLE v9_news CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;逻辑说明:CONVERT TO 会同时转换表和所有字符串列的字符集,数据内容本身也会被转换,比 MODIFY 逐个列改安全得多。第三步,处理索引长度问题。utf8mb4 下单字符占 4 字节,V9 的 title 字段 varchar(255) 若建了索引,会超出 191 字符的索引上限,需要改字段长度:
ALTER TABLE v9_news MODIFY title VARCHAR(191) NOT NULL DEFAULT '';参数说明:191 是 utf8mb4 下带索引 VARCHAR 字段的上限,来源 255*3 字节除以 4 向下取整。如果 title 没索引,这个修改不必做,但多数 V9 内容表都带了 title 索引,稳妥起见统一处理。
改造完成后,录一条带 emoji 的标题,后台保存、前台列表、详情页三处显示一致,再跑一遍中文搜索确认分词正常。如果搜索失效,说明关键词表还没转,把 v9_search_keyword、v9_keyword 相关的表一起转掉。这个技巧我在两个内容站上用过,最稳的顺序是先连接层、再库、再表,每次改完立即前台刷新验证,不要在周五傍晚动手。备份永远是第一位的,改字符集前一定先导出一份全量 SQL,否则转换过程如果中途中断,表结构恢复是件麻烦事。
老项目维护,终归是稳字当头。希望帮到你。
本文还有配套的精品资源,点击获取