PHP __callStatic() 拿不到参数的情况

Posted 知其黑、受其白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP __callStatic() 拿不到参数的情况相关的知识,希望对你有一定的参考价值。

阅读目录

php 单例

单例模式的三个要点:

1 需要一个保存类的唯一实例的静态成员变量:private static $_instance;

2 构造函数和克隆函数必须声明为私有的,防止外部程序 new 类从而失去单例模式的意义;

3 必须提供一个访问这个实例的公共的静态方法,从而返回唯一实例的一个引用 。

<?php
class Demo 
	//静态变量私有化,防止类外修改
    private static $_instance;
    public $version;
	
	//构造函数私有化,类外不能直接新建对象
    private function __construct()
        // self::$_instance = $this;
    

	//在__clone前用private修饰,用来禁止克隆
    private function __clone()
    

    public function test($ver = 1)
        $this->version = $ver;
    

    // $new=true 时将传出一个新的实例
    public static function instance($new = false)
    
        if ($new) 
            return new self();
        
        if (empty(self::$_instance)) 
            self::$_instance = new self();
        
        return self::$_instance;
    



// 由于 $this 指向的是当前对象,  self 指向的是类, 因此  self::$_instance = $this; 会将之后的实例给覆盖掉

$ins1 = Demo::instance();
$ins1->test(3);
echo $ins1->version . PHP_EOL;

$ins3 = Demo::instance(true);
$ins3->test();
echo $ins3->version . PHP_EOL;

$ins2 = Demo::instance();
echo $ins2->version . PHP_EOL;

echo $ins2->version . PHP_EOL;

输出:

3
1
3
3

由于我当时的画蛇添足, 在构造函数里加了一行 self::$_instance = $this; 导致同样的代码输出却是:

3
1
1
1

后来想了想为什么, 回忆一下得出一个答案, 就是 $this和self 的区别:

$this 可以说是当前对象的标识符,self 是当前类的标识符。

self::$_instance = $this; 时, 代表着把当前对象覆盖到类的静态属性中,静态属性是和类一起共有同一片内存的,,那么当 ins3 里新生成对象的时候,把原有的类属性给覆盖掉了,因此之后再使用 Demo::instance() 时获得的对象就和 $ins3 之前的不一样了。

从这里看 static 这个关键词使用要慎重, 使用场景只有读的情况可以随意使用, 还能节省一点内存(毕竟每个对象查到的属性都是从同一片内存中读的), 但是涉及到 读写 的时候, 就要想明白它的运行顺序了。

php 的 __callStatic 函数

现在很多框架中调用方法都是Foo::bar()这种格式的,但是他们真的是静态方法吗?

larave 中尤其常见,但是开发过程中很明显这些有一部分不是静态的,比如你使用一个模型User,那么你每次实例化出来他都是一个全新的,互不影响,这里就用到了一个魔术方法__callStatic。

举个栗子:

<?php
class Test
    public function __call($name, $arguments)
    
        echo 'this is __call'. PHP_EOL;
    

    public static function __callStatic($name, $arguments)
    
        echo 'this is __callStatic:'. PHP_EOL;
    


$test = new Test();
$test->hello();
$test::hi();
//输出:this is __call:hello
//输出:this is __callStatic:hi

当然魔术方法也是很耗性能的一种方式,每次调用的时候后会先扫一遍 class 没找到方法时才会调用它,而为了代码的整洁和抽象这个方法也能给很大的帮助,在这之间去要有个权衡。

下面实现的 log 类,采用的就是这种方法,将方法解耦出来,只要符合规定的接口就能调用

<?php

class Test
    //获取 logger 的实体
    private static $logger;

    public static function getLogger()
        return self::$logger?: self::$logger = self::createLogger();
    

    private static function createLogger()
        return new Logger();
    

    public static function setLogger(LoggerInterface $logger)
        self::$logger = $logger;
    


    public function __call($name, $arguments)
    
        call_user_func_array([self::getLogger(),$name],$arguments);
    

    public static function __callStatic($name, $arguments)
    
        forward_static_call_array([self::getLogger(),$name],$arguments);
    


interface LoggerInterface
    function info($message,array $content = []);
    function alert($messge,array $content = []);


class Logger implements LoggerInterface 
    function info($message, array $content = [])
    
        echo 'this is Log method info' . PHP_EOL;
        var_dump($content);
    

    function alert($messge, array $content = [])
    
        echo 'this is Log method alert: '. $messge . PHP_EOL;
    



Test::info('喊个口号:',['好好','学习','天天','向上']);
$test = new Test();
$test->alert('hello');

PHP __callStatic() 拿不到参数的情况

<?php

class Test

    // 实例
    protected static $instance;
    
    public static function make($rules = [], $message = [], $field = [])
    
		/*
		var_dump(self::$instance); null
		var_dump($rules, $message, $field);
		array(0) 
		
		array(0) 
		
		array(0) 
		
		*/
        if (is_null(self::$instance)) 
            self::$instance = new self($rules, $message, $field);
        
		/*
		var_dump($rules, $message, $field);
		array(0) 
		
		array(0) 
		
		array(0) 
		
		*/
        return self::$instance;
    
    
    protected function is($value, $rule, $data = [])
    
        var_dump($value, $rule, $data);
		/*
		int(555)
		string(5) "email"
		array(0) 
		*/
    
    
    public static function __callStatic($method, $params)
    
        /*
		var_dump($method, $params);
		array(2) 
		  [0]=>
		  int(555)
		  [1]=>
		  string(5) "email"
		
		*/ 
        $class = self::make();
		// var_dump(method_exists($class, $method)); true
        if (method_exists($class, $method)) 
            return call_user_func_array([$class, $method], $params);
         else 
            throw new \\BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method);
        
    


Test::is(555, 'email');

本地环境 php7.4.28

成功获取参数

array(2)  [0]=> int(555) [1]=> string(5) "email" 

生产环境 php 7.3.0

获取参数失败

array(2)  

原因

PHP 7 ChangeLog 7.3.2

https://www.php.net/ChangeLog-7.php#7.3.2



Bug #77339 __callStatic may get incorrect arguments
https://bugs.php.net/bug.php?id=77339

call_user_func_array (内置函数)

call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数。

语法

call_user_func_array(callable $callback, array $args): mixed

把第一个参数作为回调函数(callback)调用,
把参数数组作(args)为回调函数的的参数传入。

参数

callback
被调用的回调函数。

args
要被传入回调函数的数组,这个数组得是索引数组。

函数调用方式

<?php 
//单个参数
function LovePhper($app)
	echo 'Love for - '.$app;
 
/方法名参数///   方法内多个参数使用数组
call_user_func('LovePhper','PHP');

************************* 输出 *************************
Love for - PHP

//多个参数
function LikePhper(... $app)
	$str =  'Like for - ';
	foreach ($app as $key => $value) 
		echo $str,$value,PHP_EOL;
	
 
call_user_func_array('LikePhper', ['PHP','JAVA','Python','Dart'])

************************* 输出 *************************
Like for - PHP
Like for - JAVA
Like for - Python
Like for - Dart

调用类的内部方法

<?php 
class Cladr
	static function LovePhper($app)
		echo 'Love for - '.$app;
	

call_user_func_array(array('Cladr','LovePhper'),['PHP'])

*************************输出*************************
Love for - PHP

命名空间使用

<?php 
namespace Lj;

class Cladr
	static function LovePhper($app)
		echo 'Love for - namespace - '.$app;
	

call_user_func_array(array(__NAMESPACE__.'\\Cladr','LovePhper'),['PHP'])


 *************************输出*************************
 Love for - namespace - PHP

完整的函数作为回调传入 call_user_func_array()

<?php 
$LovePhper = function($app)
	return 'Love for - - '.$app;
;
echo call_user_func_array($LovePhper, ['PHP']);

 *************************输出*************************
 Love for - - PHP

以上是关于PHP __callStatic() 拿不到参数的情况的主要内容,如果未能解决你的问题,请参考以下文章

PHP中的__call和__callStatic方法(未看完)

php中__call() 和 __callStatic方法的使用

php中__call() 和 __callStatic方法的使用

PHP魔术方法之__call与__callStatic方法

PHP中的魔术方法总结 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, __unset , __sleep

[李景山php]每天TP5-20161218|thinkphp5-__callStatic函数使用