如何在 CodeIgniter controller 中向外部 class 传值
结论
支付网关文件应封装为普通 PHP 类,通过构造函数或方法参数接收 controller 传入的数据,不需要再创建一个 controller。
在 CodeIgniter 3 中,可以把这个类放到 application/libraries/,再由 controller 使用 $this->load->library() 加载并传入参数。
原来的写法为什么无法正常传值
原代码主要有以下问题:
- 类属性需要使用
public、protected或private声明。 $networkOnlineArray定义在类内部,却在类外部直接使用,超出了正确的作用域。- 类中没有声明
__construct(),实例化时传入的数组无人接收。 - 支付金额、订单信息等动态数据不应硬编码在 library 中。
- 支付密钥不宜直接写在 controller 或公开代码里,应放入配置文件或环境变量。
创建支付网关 Library
下面以 CodeIgniter 3 为例。创建文件:
application/libraries/NetworkonlieBitmapPaymentIntegration.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class NetworkonlieBitmapPaymentIntegration
{
private $settings = array();
public function __construct($params = array())
{
if (isset($params['Network_Online_setting'])) {
$this->settings = $params['Network_Online_setting'];
}
}
public function createPayment()
{
$mkey = $this->settings['MKEY'] ?? '';
$mid = $this->settings['MID'] ?? '';
$amount = $this->settings['amount'] ?? 0;
$name = $this->settings['name'] ?? '';
$currency = $this->settings['currency'] ?? 'PHP';
// 在这里调用支付网关的 integration code。
// 建议返回生成的支付参数、跳转地址或支付结果。
return array(
'MID' => $mid,
'amount' => $amount,
'name' => $name,
'currency' => $currency,
);
}
}
构造函数接收 $params 后,会把 controller 传入的数据保存在 $settings 属性中,类内其他方法可以直接使用这些值。
在 Controller 中传入参数
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Payment extends CI_Controller
{
public function checkout()
{
$params = array(
'Network_Online_setting' => array(
'MKEY' => 'GgjhJHh467HGGjjj',
'MID' => '123456789',
'amount' => 1000,
'name' => 'ADCB',
'currency' => 'PHP',
),
);
$this->load->library(
'NetworkonlieBitmapPaymentIntegration',
$params,
'payment_gateway'
);
$result = $this->payment_gateway->createPayment();
// 根据网关要求继续处理,例如加载支付页面。
$this->load->view('payment/checkout', array(
'payment' => $result,
));
}
}
$this->load->library() 的第二个参数会传给 library 的构造函数,第三个参数则是实例别名。加载后可以通过下面的属性调用该对象:
$this->payment_gateway
这样不必依赖 CodeIgniter 自动生成的属性名称。
Library 已加载时如何传递动态数据
如果支付网关对象会提前加载,而金额、订单号等数据会随每次请求变化,更适合把这些数据作为方法参数传入:
class NetworkonlieBitmapPaymentIntegration
{
private $config;
public function __construct($config = array())
{
$this->config = $config;
}
public function createPayment(array $order)
{
$payload = array(
'MID' => $this->config['MID'],
'amount' => $order['amount'],
'name' => $order['name'],
'currency' => $order['currency'],
'order_id' => $order['order_id'],
);
// 调用网关接口。
return $payload;
}
}
在 controller 中调用:
$config = array(
'MKEY' => 'GgjhJHh467HGGjjj',
'MID' => '123456789',
);
$this->load->library(
'NetworkonlieBitmapPaymentIntegration',
$config,
'payment_gateway'
);
$order = array(
'order_id' => 'ORDER-20260001',
'amount' => 1000,
'name' => 'ADCB',
'currency' => 'PHP',
);
$result = $this->payment_gateway->createPayment($order);
这种结构能清楚区分两类数据:MKEY、MID 是固定配置,订单号、金额和付款人信息则是每次支付产生的动态数据。
将密钥放入配置文件
可以创建:
application/config/payment.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$config['network_online'] = array(
'MKEY' => 'GgjhJHh467HGGjjj',
'MID' => '123456789',
);
然后在 controller 中读取:
$this->config->load('payment');
$gatewayConfig = $this->config->item('network_online');
$this->load->library(
'NetworkonlieBitmapPaymentIntegration',
$gatewayConfig,
'payment_gateway'
);
在生产环境中,密钥最好从服务器环境变量或未纳入版本控制的私有配置文件中读取。
不使用 CodeIgniter Loader
如果第三方文件不适合改造成 CodeIgniter library,也可以先引入文件,再直接实例化:
require_once APPPATH . 'third_party/payment/NetworkonlieBitmapPaymentIntegration.php';
$params = array(
'Network_Online_setting' => array(
'MKEY' => 'GgjhJHh467HGGjjj',
'MID' => '123456789',
'amount' => 1000,
'name' => 'ADCB',
'currency' => 'PHP',
),
);
$paymentGateway = new NetworkonlieBitmapPaymentIntegration($params);
$result = $paymentGateway->createPayment();
如果这个类能够正常封装,放入 application/libraries/ 并通过 CodeIgniter Loader 加载,通常会更方便管理。
注意事项
- 不要直接采用浏览器提交的支付金额。应根据订单编号从数据库重新读取并核对金额。
- 金额单位必须符合网关要求。有些网关接收主货币单位,有些则接收分等最小单位。
- 发送请求前,应检查
MID、amount、currency和订单号是否完整。 - 收到支付回调后必须验证签名,不能只根据回调参数就把订单标记为已支付。
- 不要在日志、异常页面或前端代码中输出
MKEY。 - 示例使用 CodeIgniter 3 的
$this->load->library()。如果项目使用 CodeIgniter 4,应通过命名空间、服务或直接实例化类来注入参数,因为两者的加载机制不同。
备注:内容仅供参考。
