Windows下用php-smpp库报错:call_user_func找不到printDebug函数求助
解决php-smpp库回调函数找不到的问题
问题现象
在Windows环境使用php-smpp的windows兼容版本开发SMPP短信发送功能时,触发报错:
call_user_func(): Argument #1 ($callback) must be a valid callback, function "printDebug" not found or invalid function name
问题原因
你把printDebug函数定义在sendSmsSmpp方法内部,属于局部函数,而SmppClient和TSocket类在外部调用这个回调时,PHP的函数查找机制无法定位到该局部函数,导致回调无效。
解决方案
把printDebug改为控制器类的成员方法,并以数组格式传递回调(PHP中类方法的回调格式为[$实例, '方法名'])。
修正后的控制器代码
<?php namespace App\Http\Controllers; use Exception; use GsmEncoder; use SMPP; use SmppAddress; use SmppClient; use TSocket; $GLOBALS['SMPP_ROOT'] = dirname(__FILE__); require_once $GLOBALS['SMPP_ROOT'].'/protocol/smppclient.class.php'; require_once $GLOBALS['SMPP_ROOT'].'/protocol/gsmencoder.class.php'; require_once $GLOBALS['SMPP_ROOT'].'/transport/tsocket.class.php'; class apiSmsSmppController extends Controller{ // 移为类的公共成员方法 public function printDebug($str) { echo date('Ymd H:i:s ').$str."\r\n"; } public function sendSmsSmpp($message,$tel){ try { // 传递回调改为数组格式:[$this, '方法名'] $transport = new TSocket('xxx.xxx.xx.xx',2775,false,[$this, 'printDebug']); $transport->setRecvTimeout(10000); $transport->setSendTimeout(10000); // 同样修改这里的回调参数 $smpp = new SmppClient($transport,[$this, 'printDebug']); $smpp->debug = true; $transport->setDebug(true); $transport->open(); $smpp->bindTransmitter("xxxxx","xxxx"); $message = 'H€llo world'; $encodedMessage = GsmEncoder::utf8_to_gsm0338($message); $from = new SmppAddress(GsmEncoder::utf8_to_gsm0338('SMPP Tést'),SMPP::TON_ALPHANUMERIC); $to = new SmppAddress(22670530377,SMPP::TON_INTERNATIONAL,SMPP::NPI_E164); $smpp->sendSMS($from,$to,$encodedMessage); $smpp->close(); } catch (Exception $e) { try { $smpp->close(); } catch (Exception $ue) { // 调用类内方法 $this->printDebug("Failed to unbind; '".$ue->getMessage()."' closing transport"); if ($transport->isOpen()) $transport->close(); } throw $e; } } } ?>
补充说明
smppclient.class中通过call_user_func($this->debugHandler, ...)调用回调,PHP对回调的有效性要求:
- 全局函数直接传字符串函数名
- 类实例方法传
[$实例对象, '方法名'] - 静态方法传
[类名::class, '静态方法名']
如果不想改为类方法,也可以把printDebug定义在命名空间之外(全局作用域),但在Laravel框架中推荐使用类成员方法的方式,更符合代码规范。
内容的提问来源于stack exchange,提问作者le prince
相关产品推荐
相关产品推荐

