
PHP 8 废弃了以类名命名的构造函数(如 function basket()),仅支持 __construct() 方法作为构造入口;若未及时迁移,对象实例化后构造逻辑将完全跳过,导致依赖初始化的数据(如 $cartid)为空。
php 8 废弃了以类名命名的构造函数(如 `function basket()`),仅支持 `__construct()` 方法作为构造入口;若未及时迁移,对象实例化后构造逻辑将完全跳过,导致依赖初始化的数据(如 `$cartid`)为空。
在从 PHP 7.x 升级至 PHP 8.x 的过程中,一个关键且极易被忽视的兼容性变化是:PHP 8 彻底移除了“类名即构造函数”的旧式语法支持。这意味着,像您代码中这样定义的 basket 类:
class basket
{
// ... 属性声明 ...
function basket() // ⚠️ PHP 8 中:此方法不再被识别为构造函数!
{
global $cartid;
// 初始化逻辑(读取 cookie、创建新购物车、设置 $cartid 等)
}
}在 PHP 7.4 及更早版本中,function basket() 会被自动识别为构造方法并执行;但自 PHP 8.0 起,该语法被完全废弃——该方法将被当作普通成员函数对待,不会在 new basket 时自动调用。因此,$cart = new basket; 后 $cartid 未被初始化,后续逻辑(如数据库查询、添加商品)全部失效,这正是您观察到“篮子对象未启动”的根本原因。
✅ 正确迁移方案:统一改用 __construct()
请将 sqlcart.php 中 basket 类的构造函数重写为标准魔术方法 __construct(),并移除所有 global 声明(推荐使用依赖注入或类属性管理状态,但为最小改动兼容现有逻辑,此处保留 global 并明确标注风险):
class basket
{
public $items;
public $empty;
public $cartid; // 建议:将 $cartid 同步赋值给对象属性,减少全局依赖
public $voucher_id_set;
public function __construct() // ✅ PHP 8 唯一认可的构造方法
{
global $cartid;
global $vat_rate;
global $voucher_id_set;
global $outmail;
global $conn;
global $_COOKIE;
global $_POST;
$voucher_id_set = 0;
$number = 0;
if (isset($_COOKIE["cart_id"])) {
$cartid = ClearString(substr($_COOKIE["cart_id"], 0, 10));
$testcartid = $cartid;
settype($testcartid, "integer");
if ($testcartid > 0) {
$strsql = "SELECT * FROM g_tempbasket WHERE basket_id = '" . clearstring(substr($cartid, 0, 10)) . "'";
$result = safedb_query($strsql);
$number = mysqli_num_rows($result);
}
} else {
$number = 0;
}
if ($number == 0) {
$todaydate = date("Y-m-d H:i:s"); // ⚠️ 注意:原代码用 'h'(12小时制),建议改为 'H'(24小时制)
$strsql = "INSERT INTO g_tempbasket (basket_id, date) VALUES (NULL, '$todaydate')";
safedb_query($strsql);
$newcartid = mysqli_insert_id($conn);
if ($outmail == 1) {
setcookie("cart_id", $newcartid, time() + 86400, "", ".website.co.uk", true, true);
setcookie("voucher_id", "", 0, "", ".website.co.uk", true, true);
} else {
setcookie("cart_id", $newcartid, time() + 86400, "", "", false, true);
setcookie("voucher_id", "", 0, "", "", false, true);
}
$cartid = $newcartid;
}
$strsql = "SELECT t.product_id, p.descript, p.cost, t.qty
FROM g_tempbasket AS tb, g_product AS p, g_tempitems AS t
WHERE tb.basket_id = t.basket_id
AND t.product_id = p.product_id
AND tb.basket_id = " . (int)$cartid; // ✅ 强制类型转换,防止 SQL 注入(基础防护)
$result = safedb_query($strsql);
$number = mysqli_num_rows($result);
mysqli_free_result($result);
$this->empty = ($number === 0); // 直接赋值,语义清晰
$this->cartid = $cartid; // ✅ 同步保存到对象属性,降低全局变量耦合
}
// 其余方法(如 additem)保持不变,但建议后续逐步重构以消除 global 依赖
}⚠️ 关键注意事项与最佳实践
-
全局变量风险:当前代码重度依赖
global $cartid,极易引发作用域混乱和并发问题。强烈建议后续重构为:立即学习“PHP免费学习笔记(深入)”;
- 将
$cartid作为__construct()的可选参数传入; - 或通过依赖注入传递数据库连接
$conn和配置项; - 最终目标:消除
global,提升可测试性与可维护性。
- 将
SQL 安全加固:示例中已对
$cartid添加(int)强制转换,但完整方案应使用 预处理语句(Prepared Statements) 替代字符串拼接,彻底杜绝 SQL 注入。Cookie 安全选项:
setcookie()已补充httponly=true和secure=true(生产环境需 HTTPS),符合现代安全规范。-
PHP 8 其他潜在陷阱:
- 检查是否使用
mysql_*函数(已彻底移除),确保全部替换为mysqli_*或 PDO; - 验证
count(null)等弱类型操作,PHP 8 对类型错误更严格; - 运行
php -l检查语法,并启用error_reporting(E_ALL)捕获隐式警告。
- 检查是否使用
完成上述修改后,$cart = new basket; 将正确触发初始化逻辑,$cartid 可被正常访问,购物车功能即可在 PHP 8.x 下稳定运行。迁移不仅是语法适配,更是借机清理技术债、提升系统健壮性的契机。



















