目的

想要通过字符变量来控制调用的 A 类或 B 类的方法,进而可以控制调用的对象。现实案例就是短信目前对接的是网易云信,想要再接入七牛云和阿里云的短信作为补充。

过程

一开始进入脑海就是:

$sms = 'Sms';
$sms::sendCode($mobile);

因为我记得之前看过这样使用的,但实际使用却会报错:Class 'Sms' not found。之后再网上搜索 PHP 使用字符串调用类 相关的内容,发现大多的博文里都会提到一个 call_user_func 方法,类似的解是这样的:

call_user_func(array($class_name, 'doSomething'));
call_user_func($class_name .'::doSomething'); // >5.2.3

直接用类名和方法名替换,依然是不行的,还是提示找不到类。

找到 PHP 函数手册里 call_user_func 的定义,主要是看下面的使用案例。Example #3 call_user_func() using namespace name 里说明了使用命名空间的情况,看到这立马理解了问题所在。

$sms = __namespace__ . '\\' . 'Sms';
call_user_func(array($class_name, 'sendCode'), $mobile);
call_user_func($class_name .'::sendCode', $mobile); // >5.2.3

这是 Sms 类与当前类在同一命名空间的情况,其他情况可以修改 __namespace__

然后使用 new $className() 其实也是一样的:

$sms = __namespace__ . '\\' . 'Sms';
$sms_instance = new $sms();
$sms_instance::sendCode($mobile);

也是这里我意识到之前一直没有注意到问题:PHP 类的实例是可以调用静态方法的。这让我有点恍惚,我印象中静态方法只能通过类名::方法名方式调用,或者 self::static::,难道是 java 或者 python 给我带来的错觉吗?不管了。

最后使用最开始的方法尝试,也是可以的:

$sms = __namespace__ . '\\' . 'Sms';
$sms::sendCode($mobile);

使用变量替换方法名:

# 直接替换
$sms = 'sendCode';
Sms::$sms($mobile);

# 使用 new 对象
$sms = __namespace__ . '\\' . 'Sms';
$sms_instance = new $sms();
$sms_instance->{'sendCode'}($mobile);

# 使用 call_user_func
call_user_func(array($sms, 'sendCode'), $mobile);
文章目录