https://github.com/coowinit/wordpress-facebook-ads-form-tracking
一个用于 WordPress 自定义表单的 Facebook 广告询盘来源追踪方案,可在邮件中显示 Facebook Ads: Yes/No。
https://github.com/coowinit/wordpress-facebook-ads-form-tracking
ajax-form facebook-ads form-tracking lead-tracking meta-ads utm-tracking wordpress wordpress-theme wp-mail
Last synced: 23 days ago
JSON representation
一个用于 WordPress 自定义表单的 Facebook 广告询盘来源追踪方案,可在邮件中显示 Facebook Ads: Yes/No。
- Host: GitHub
- URL: https://github.com/coowinit/wordpress-facebook-ads-form-tracking
- Owner: coowinit
- Created: 2026-04-29T01:59:55.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-29T05:19:59.000Z (2 months ago)
- Last Synced: 2026-04-29T05:24:14.940Z (2 months ago)
- Topics: ajax-form, facebook-ads, form-tracking, lead-tracking, meta-ads, utm-tracking, wordpress, wordpress-theme, wp-mail
- Language: PHP
- Homepage: https://coowinit.github.io/wordpress-facebook-ads-form-tracking/
- Size: 25.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# WordPress 表单 Facebook 广告来源追踪指南
这是一个适用于 WordPress 自定义主题表单的简化版 Facebook / Meta 广告来源追踪方案。
目标很简单:
> 用户从 Facebook 广告进入网站并提交表单后,询盘邮件中显示 `Facebook Ads: Yes`;非广告来源则显示 `Facebook Ads: No`。
同时,本方案还会记录:
- 用户第一次进入网站的页面
- 带有 `fb_ads=1` 参数的 Facebook 广告落地页
- 用户最终提交表单的页面
---
## 适用场景
适合以下情况:
- WordPress 自定义主题
- 表单通过 Ajax 提交
- 表单最终通过 `wp_mail()` 发送到邮箱
- 不想接入复杂的 GA4 / Google Tag Manager / Meta Pixel 转化统计
- 只想在邮件里快速判断询盘是否来自 Facebook 广告
---
## 实现原理
广告链接中添加一个最简单的参数:
```txt
fb_ads=1
```
例如:
```txt
https://www.example.com/contact-us/?fb_ads=1
```
或者:
```txt
https://www.example.com/product-page/?fb_ads=1
```
当用户访问带有 `fb_ads=1` 的页面时,前端 JavaScript 会把来源信息保存到浏览器的 `sessionStorage` 中;为了兼容旧版本,也可以同时读取 `localStorage`。
这样即使用户不是在广告落地页直接提交表单,而是先浏览其他页面,再进入 Contact 页面提交表单,也仍然可以识别为 Facebook 广告来源。
示例路径:
```txt
1. 用户点击 Facebook 广告
2. 进入 https://www.example.com/product-page/?fb_ads=1
3. 浏览其他页面
4. 进入 https://www.example.com/contact-us/
5. 提交表单
6. 邮件中显示 Facebook Ads: Yes
```
### 最终增强版推荐架构
经过多表单测试后,推荐使用下面这套更稳的组合:
| 模块 | 作用 |
|---|---|
| 前端 `sessionStorage` | 记录本次浏览会话中的 Facebook 广告来源 |
| 全站 `wp_footer` 脚本 | 自动给页面里的所有表单补充隐藏字段 |
| `window.evodekFbAdsTracking` | 给免费样品页、多步骤表单、手动 `new FormData()` 的 JS 读取使用 |
| PHP Session | 服务器端兜底,防止部分表单漏传隐藏字段 |
| 统一邮件函数 | 所有表单邮件统一输出 `Facebook Ads Tracking` 区块 |
这种方式可以覆盖:
- Contact Us / 代理商表单
- 购物车提交表单
- Decking Calculator 表单
- Cladding Calculator 表单
- Free Samples 免费样品多步骤表单
---
## 增强版:服务器端 PHP Session 兜底
如果主题里已经有购物车功能,并且已经启用了 PHP Session,可以直接复用。
如果没有,可以先添加:
```php
add_action('init', 'theme_start_session', 1);
function theme_start_session() {
if (!session_id()) {
session_start();
}
}
```
然后添加服务器端兜底记录:
```php
add_action('init', 'theme_capture_fb_ads_tracking_server_side', 2);
function theme_capture_fb_ads_tracking_server_side() {
if (is_admin()) {
return;
}
if (!isset($_SESSION) || !is_array($_SESSION)) {
return;
}
$current_url = esc_url_raw(home_url(add_query_arg(array(), $_SERVER['REQUEST_URI'] ?? '/')));
if (empty($_SESSION['theme_first_landing_page'])) {
$_SESSION['theme_first_landing_page'] = $current_url;
}
if (isset($_GET['fb_ads']) && sanitize_text_field(wp_unslash($_GET['fb_ads'])) === '1') {
$_SESSION['theme_facebook_ads'] = 'Yes';
$_SESSION['theme_fb_ads_landing_page'] = $current_url;
}
}
```
这一步的作用是:即使某个 Ajax 表单没有把隐藏字段提交到后台,PHP 仍然可以尝试从 Session 中识别广告来源。
---
## 增强版:自动给所有表单补隐藏字段
如果网站有很多表单页面,不想每个页面手动添加隐藏域,可以让全站脚本自动补充字段。
核心逻辑如下:
```js
var trackingFieldNames = [
'facebook_ads',
'first_landing_page',
'fb_ads_landing_page'
];
function ensureTrackingFieldsInForms() {
var forms = document.querySelectorAll('form');
forms.forEach(function (form) {
trackingFieldNames.forEach(function (name) {
if (!form.querySelector('[name="' + name + '"]')) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.className = 'theme-fb-ads-tracking-field';
form.appendChild(input);
}
});
});
}
```
同时建议在全站脚本里暴露一个全局对象:
```js
window.evodekFbAdsTracking = {
facebook_ads: facebookAds,
first_landing_page: firstLandingPage,
fb_ads_landing_page: fbAdsLandingPage
};
```
这样免费样品页这类独立 JS 文件也能读取到广告来源数据。
---
## Meta / Facebook 广告后台参数设置
如果广告落地页是:
```txt
https://www.example.com/contact-us/
```
完整 URL 可以写成:
```txt
https://www.example.com/contact-us/?fb_ads=1
```
如果 Meta 广告后台有单独的 **URL Parameters** 输入框,只需要填写:
```txt
fb_ads=1
```
不需要填写复杂的 UTM 参数。
---
## 最终邮件显示效果
Facebook 广告来的询盘:
```txt
Facebook Ads: Yes
First Landing Page: https://www.example.com/product-page/?fb_ads=1
Facebook Ads Landing Page: https://www.example.com/product-page/?fb_ads=1
Submitted Page URL: https://www.example.com/contact-us/
```
非 Facebook 广告来的询盘:
```txt
Facebook Ads: No
First Landing Page: https://www.example.com/contact-us/
Facebook Ads Landing Page:
Submitted Page URL: https://www.example.com/contact-us/
```
---
## 第一步:在 functions.php 中加入全站追踪脚本
将以下代码添加到主题的 `functions.php` 文件中。
建议放在表单相关函数附近,或者文件底部。
```php
/**
* 全站记录 Facebook 广告来源
*
* 广告链接示例:
* https://www.example.com/contact-us/?fb_ads=1
*/
add_action('wp_footer', 'theme_output_fb_ads_tracking_script');
function theme_output_fb_ads_tracking_script() {
if (is_admin()) {
return;
}
?>
(function () {
var params = new URLSearchParams(window.location.search);
var hasFbAdsParam = params.get('fb_ads') === '1';
var currentUrl = window.location.href;
// 记录用户第一次进入网站的页面
if (!localStorage.getItem('first_landing_page')) {
localStorage.setItem('first_landing_page', currentUrl);
}
// 如果当前页面 URL 中带有 fb_ads=1,则标记为 Facebook 广告来源
if (hasFbAdsParam) {
localStorage.setItem('facebook_ads', 'Yes');
localStorage.setItem('fb_ads_landing_page', currentUrl);
} else if (!localStorage.getItem('facebook_ads')) {
localStorage.setItem('facebook_ads', 'No');
}
function setField(id, value) {
var field = document.getElementById(id);
if (field) {
field.value = value || '';
}
}
// 自动填充表单隐藏字段
setField('facebook_ads', localStorage.getItem('facebook_ads') || 'No');
setField('first_landing_page', localStorage.getItem('first_landing_page') || '');
setField('fb_ads_landing_page', localStorage.getItem('fb_ads_landing_page') || '');
})();
```
示例:
```html
Submit
```
如果你的表单使用的是 jQuery Ajax,并通过下面这种方式收集字段:
```js
var formData = $form.serializeArray();
```
那么新增的隐藏字段会自动随表单一起提交,不需要额外处理。
---
## 第三步:Ajax 提交时保留最终提交页面 URL
如果你的 Ajax 里已经有类似代码,可以保留:
```js
formData.push({ name: 'page_url', value: window.location.href });
```
完整示例:
```js
jQuery(function($){
$('#contactus').on('submit', function(e){
e.preventDefault();
var $form = $(this);
var $btn = $form.find('button[type="submit"]');
if ($btn.prop('disabled')) {
return;
}
var formData = $form.serializeArray();
formData.push({ name: 'action', value: 'send_contact_us_email' });
formData.push({ name: 'page_url', value: window.location.href });
$.ajax({
url: ajaxurl_or_admin_ajax_url,
type: 'POST',
dataType: 'json',
data: $.param(formData),
beforeSend: function(){
$btn.prop('disabled', true).text('Sending...');
},
success: function(res){
if (res.success) {
alert(res.data.msg || 'Submitted successfully.');
$form[0].reset();
} else {
alert(res.data && res.data.msg ? res.data.msg : 'Submission failed.');
}
},
error: function(){
alert('Network or server error, please try again later.');
},
complete: function(){
$btn.prop('disabled', false).text('Submit');
}
});
});
});
```
注意:`ajaxurl_or_admin_ajax_url` 需要替换成你主题中的 Ajax 地址,例如:
```php
```
---
---
## 第三步补充:手动 `new FormData()` 的表单需要额外处理
有些复杂表单并不是直接提交整个 ``,也不是用:
```js
var formData = $form.serializeArray();
```
而是像免费样品页这种逻辑,在独立 JS 文件中手动创建:
```js
const formData = new FormData();
```
然后逐个添加字段:
```js
formData.append('action', 'evodek_submit_sample_request');
formData.append('first_name', payload.first_name || '');
formData.append('email', payload.email || '');
```
这种写法需要特别注意:
> 即使页面里已经有隐藏字段,如果 JS 没有把这些字段 `append()` 进去,Ajax 请求也不会提交这些 Facebook Ads 数据。
### 免费样品页这类表单的推荐处理方式
在独立 JS 文件中增加一个读取追踪数据的方法:
```js
function getFbAdsTrackingData() {
function getFieldValue(fieldName) {
const field = document.querySelector(`[name="${fieldName}"], #${fieldName}`);
return field ? field.value : '';
}
const globalTracking = window.themeFbAdsTracking || window.evodekFbAdsTracking || {};
return {
facebook_ads:
getFieldValue('facebook_ads') ||
globalTracking.facebook_ads ||
sessionStorage.getItem('facebook_ads') ||
localStorage.getItem('facebook_ads') ||
'No',
first_landing_page:
getFieldValue('first_landing_page') ||
globalTracking.first_landing_page ||
sessionStorage.getItem('first_landing_page') ||
localStorage.getItem('first_landing_page') ||
window.location.href,
fb_ads_landing_page:
getFieldValue('fb_ads_landing_page') ||
globalTracking.fb_ads_landing_page ||
sessionStorage.getItem('fb_ads_landing_page') ||
localStorage.getItem('fb_ads_landing_page') ||
''
};
}
```
然后在构建 Ajax `FormData` 时追加这 3 个字段:
```js
const fbAdsTracking = getFbAdsTrackingData();
formData.append('facebook_ads', fbAdsTracking.facebook_ads === 'Yes' ? 'Yes' : 'No');
formData.append('first_landing_page', fbAdsTracking.first_landing_page || '');
formData.append('fb_ads_landing_page', fbAdsTracking.fb_ads_landing_page || '');
```
完整示例:
```js
function buildAjaxFormData(payload) {
const formData = new FormData();
formData.append('action', 'evodek_submit_sample_request');
formData.append('sample_request_nonce', ajaxNonce);
formData.append('first_name', payload.first_name || '');
formData.append('last_name', payload.last_name || '');
formData.append('phone', payload.phone || '');
formData.append('email', payload.email || '');
formData.append('message', payload.message || '');
formData.append('page_url', window.location.href);
formData.append('user_agent', navigator.userAgent || '');
const fbAdsTracking = getFbAdsTrackingData();
formData.append('facebook_ads', fbAdsTracking.facebook_ads === 'Yes' ? 'Yes' : 'No');
formData.append('first_landing_page', fbAdsTracking.first_landing_page || '');
formData.append('fb_ads_landing_page', fbAdsTracking.fb_ads_landing_page || '');
return formData;
}
```
### 判断规则
| 表单提交方式 | 是否自动带隐藏字段 | 是否需要手动 append |
|---|---:|---:|
| 普通表单提交 | 是 | 否 |
| `serializeArray()` | 是 | 否 |
| `FormData(form)` | 是 | 否 |
| `new FormData()` 后逐个 `append()` | 否 | 是 |
---
## 第三步补充:更新独立 JS 文件后要处理缓存
如果你修改了独立 JS 文件,例如:
```txt
/js/evodek-sample-request.js
```
浏览器或缓存插件可能仍然加载旧版本,导致新加的 Facebook Ads 字段没有生效。
### 简单做法:手动修改版本号
原来可能是:
```php
```
修改 JS 后,把版本号改成新的:
```php
```
### 推荐做法:使用 `filemtime()` 自动刷新版本
更推荐用文件修改时间作为版本号:
```php
```
这样每次你上传并覆盖 `evodek-sample-request.js` 后,只要文件修改时间变了,前端就会自动加载新版 JS。
### WordPress enqueue 写法
如果你的主题使用 `wp_enqueue_script()`,可以这样写:
```php
add_action('wp_enqueue_scripts', 'theme_enqueue_sample_request_script');
function theme_enqueue_sample_request_script() {
$file = 'js/evodek-sample-request.js';
$path = get_theme_file_path($file);
$ver = file_exists($path) ? filemtime($path) : '1.0.0';
wp_enqueue_script(
'evodek-sample-request',
get_theme_file_uri($file),
[],
$ver,
true
);
}
```
### 测试时的建议
修改 JS 后建议同时做这几件事:
1. 页面源码中确认 JS 地址后面的 `v=` 已经变化。
2. 浏览器按 `Ctrl + F5` 强制刷新。
3. 使用无痕窗口测试。
4. 如果网站有缓存插件或 CDN,清理页面缓存和静态资源缓存。
---
## 增强版:统一 PHP 邮件输出函数
建议把 Facebook Ads 邮件输出封装成统一函数,然后所有表单邮件都调用它。
```php
function theme_get_fb_ads_tracking_data_from_post() {
$posted_facebook_ads = sanitize_text_field($_POST['facebook_ads'] ?? 'No');
$posted_first_landing_page = esc_url_raw($_POST['first_landing_page'] ?? '');
$posted_fb_ads_landing_page = esc_url_raw($_POST['fb_ads_landing_page'] ?? '');
$posted_page_url = esc_url_raw($_POST['page_url'] ?? '');
$session_facebook_ads = sanitize_text_field($_SESSION['theme_facebook_ads'] ?? 'No');
$session_first_landing_page = esc_url_raw($_SESSION['theme_first_landing_page'] ?? '');
$session_fb_ads_landing_page = esc_url_raw($_SESSION['theme_fb_ads_landing_page'] ?? '');
$first_landing_page = $posted_first_landing_page ?: $session_first_landing_page;
$fb_ads_landing_page = $posted_fb_ads_landing_page ?: $session_fb_ads_landing_page;
$facebook_ads = ($posted_facebook_ads === 'Yes' || $session_facebook_ads === 'Yes') ? 'Yes' : 'No';
$combined_urls = $posted_page_url . ' ' . $first_landing_page . ' ' . $fb_ads_landing_page;
if ($facebook_ads !== 'Yes' && strpos($combined_urls, 'fb_ads=1') !== false) {
$facebook_ads = 'Yes';
if (!$fb_ads_landing_page) {
$fb_ads_landing_page = $posted_page_url ?: $first_landing_page;
}
}
return [
'facebook_ads' => $facebook_ads,
'first_landing_page' => $first_landing_page,
'fb_ads_landing_page' => $fb_ads_landing_page,
];
}
function theme_render_email_url_line($label, $url) {
$url = esc_url_raw($url);
if (!$url) {
return '
' . esc_html($label) . ':
';
}
return '
' . esc_html($label) . ': ' . esc_html($url) . '
';
}
function theme_get_fb_ads_tracking_email_html() {
$tracking = theme_get_fb_ads_tracking_data_from_post();
return ''
. '
'
. 'Facebook Ads Tracking
'
. 'Facebook Ads: ' . esc_html($tracking['facebook_ads']) . '
'
. theme_render_email_url_line('First Landing Page', $tracking['first_landing_page'])
. theme_render_email_url_line('Facebook Ads Landing Page', $tracking['fb_ads_landing_page']);
}
```
邮件正文里只需要加入:
```php
. theme_get_fb_ads_tracking_email_html()
```
如果你的项目已经使用 `evodek_get_fb_ads_tracking_email_html()` 这个函数名,也可以继续沿用原来的命名。
## 第四步:在 PHP 邮件函数中接收字段
在处理表单提交的 PHP 函数中,添加以下字段接收代码。
```php
$facebook_ads = sanitize_text_field($_POST['facebook_ads'] ?? 'No');
$first_landing_page = esc_url_raw($_POST['first_landing_page'] ?? '');
$fb_ads_landing_page = esc_url_raw($_POST['fb_ads_landing_page'] ?? '');
$page_url = esc_url_raw($_POST['page_url'] ?? '');
```
建议对 `facebook_ads` 做一次规范化处理,避免异常值:
```php
$facebook_ads = ($facebook_ads === 'Yes') ? 'Yes' : 'No';
```
---
## 第五步:把 Facebook 来源信息加入邮件内容
在邮件正文 `$body` 中加入以下内容。
```php
$body .= ''
. '
'
. '
Facebook Ads: ' . esc_html($facebook_ads) . '
'
. 'First Landing Page: '
. ($first_landing_page ? '' . esc_html($first_landing_page) . '' : '')
. '
'
. 'Facebook Ads Landing Page: '
. ($fb_ads_landing_page ? '' . esc_html($fb_ads_landing_page) . '' : '')
. '
'
. 'Submitted Page URL: '
. ($page_url ? '' . esc_html($page_url) . '' : '')
. '
';
```
如果你原本已经有 `Page URL` 字段,也可以改名为 `Submitted Page URL`,这样更清楚。
---
## 推荐邮件字段结构
建议邮件中至少保留这些信息:
```txt
Name
Email
Phone
State
Subject
Message
IP Address
Facebook Ads
First Landing Page
Facebook Ads Landing Page
Submitted Page URL
Submitted At
```
含义说明:
| 字段 | 含义 |
|---|---|
| Facebook Ads | 是否来自 Facebook 广告 |
| First Landing Page | 用户第一次进入网站的页面 |
| Facebook Ads Landing Page | 带有 `fb_ads=1` 的广告落地页 |
| Submitted Page URL | 用户最终提交表单的页面 |
| IP Address | 用户 IP |
| Submitted At | 提交时间 |
---
## 测试方法
### 测试 1:直接在 Contact 页面提交
访问:
```txt
https://www.example.com/contact-us/?fb_ads=1
```
提交表单后,邮件中应该显示:
```txt
Facebook Ads: Yes
First Landing Page: https://www.example.com/contact-us/?fb_ads=1
Facebook Ads Landing Page: https://www.example.com/contact-us/?fb_ads=1
Submitted Page URL: https://www.example.com/contact-us/?fb_ads=1
```
---
### 测试 2:先进入产品页,再进入 Contact 页面
访问:
```txt
https://www.example.com/product-page/?fb_ads=1
```
然后手动点击进入:
```txt
https://www.example.com/contact-us/
```
提交表单后,邮件中应该显示:
```txt
Facebook Ads: Yes
First Landing Page: https://www.example.com/product-page/?fb_ads=1
Facebook Ads Landing Page: https://www.example.com/product-page/?fb_ads=1
Submitted Page URL: https://www.example.com/contact-us/
```
---
### 测试 3:普通访问
访问:
```txt
https://www.example.com/contact-us/
```
提交表单后,邮件中应该显示:
```txt
Facebook Ads: No
```
---
## 测试时的注意事项
因为本方案会使用浏览器的 `sessionStorage`,部分旧版本也可能使用 `localStorage`,测试时需要注意:
如果你已经访问过带有 `fb_ads=1` 的链接,当前浏览器会一直记住 `Facebook Ads: Yes`。
重新测试普通流量时,可以:
1. 使用无痕窗口测试
2. 或者清除浏览器缓存 / localStorage
3. 或者在浏览器控制台执行:
```js
localStorage.removeItem('facebook_ads');
localStorage.removeItem('first_landing_page');
localStorage.removeItem('fb_ads_landing_page');
// 如果你的项目使用 sessionStorage,则执行:
sessionStorage.removeItem('facebook_ads');
sessionStorage.removeItem('first_landing_page');
sessionStorage.removeItem('fb_ads_landing_page');
```
---
## 常见问题
### 1. 只用 `fb_ads=1`,不用 UTM,可以吗?
可以。
如果只是为了在邮箱里判断是否来自 Facebook 广告,`fb_ads=1` 已经足够。
---
### 2. 这个能区分具体广告系列、广告组、广告名称吗?
不能。
这个方案只判断:
```txt
是不是 Facebook 广告来的
```
如果后期需要区分广告系列、广告组、广告名称,可以升级为 UTM 方案,例如:
```txt
utm_source=facebook&utm_medium=paid_social&utm_campaign=xxx&utm_content=xxx
```
---
### 3. 用户跳转多个页面后还能识别吗?
可以。
因为广告标记会保存到 `sessionStorage`,复杂表单还可以通过 PHP Session 做服务器端兜底。
只要用户使用同一个浏览器,在没有清除浏览器数据的情况下,后续进入 Contact 页面提交表单,仍然可以识别。
---
### 4. 用户换浏览器或换设备后还能识别吗?
不能。
`sessionStorage` / `localStorage` 只保存在当前浏览器和当前设备中。
---
### 5. 这个能替代 Meta Pixel 吗?
不能完全替代。
这个方案主要用于在询盘邮件中标记来源,方便人工查看。
Meta Pixel 更适合做广告后台转化归因、再营销和广告优化。
---
## 后期添加到其他网站的步骤
1. 在 Facebook / Meta 广告链接中添加:
```txt
fb_ads=1
```
2. 在目标 WordPress 主题的 `functions.php` 中加入全站追踪脚本。
3. 在表单页面加入 3 个隐藏字段:
```html
```
4. 确认 Ajax 提交时包含:
```js
formData.push({ name: 'page_url', value: window.location.href });
```
5. 在 PHP 邮件函数中接收字段。
6. 在邮件正文中输出:
```txt
Facebook Ads
First Landing Page
Facebook Ads Landing Page
Submitted Page URL
```
7. 如果表单使用独立 JS 且手动 `new FormData()`,记得把 `facebook_ads`、`first_landing_page`、`fb_ads_landing_page` 手动 `append()` 进去。
8. 修改独立 JS 后,更新版本号,例如 `?v=1.0.4`,或使用 `filemtime()` 自动刷新版本。
9. 使用无痕窗口进行测试。
---
## 最终检查清单
把这套方案复用到其他网站时,建议逐项检查:
1. Facebook / Meta 广告链接是否带有 `fb_ads=1`。
2. 主题是否已经启用 PHP Session。
3. `functions.php` 是否有服务器端兜底记录逻辑。
4. 全站 `wp_footer` 脚本是否能正常输出。
5. 表单隐藏字段是否存在,或者是否能自动注入。
6. 所有邮件正文是否调用统一的 Facebook Ads 邮件输出函数。
7. 普通 Ajax 表单是否使用 `serializeArray()` 或 `FormData(form)`。
8. 手动 `new FormData()` 的表单是否手动 append 了 3 个字段。
9. 独立 JS 更新后,是否修改版本号或使用 `filemtime()`。
10. 是否用无痕窗口分别测试广告流量和普通流量。
---
## 安全提醒
如果你的 `functions.php` 中包含 SMTP 邮箱、授权码、API Key 等敏感信息,不要上传到公开 GitHub 仓库。
公开示例代码时建议替换成占位符:
```php
$phpmailer->Username = 'your-email@example.com';
$phpmailer->Password = 'your-smtp-app-password';
```