PHP合并数组操作“”+”与array_merge的区别

这个也算是自己在编码中碰到的一个坑

1、键名为数字(数字型字符串)时,array_merge()不会覆盖掉原来的值,但“+”合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉

$arr1 = array('a','b');
$arr2 = array('c', 'd');
var_dump($arr1+$arr2);
//输出
array(2) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
}
var_dump(array_merge($arr1, $arr2));
//输出
array(4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
}

2、键名为字符(非数字型字符)时,+仍然把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉,但array_merge()此时会覆盖掉前面相同键名的值
$arr1 = array(0 => 'a', 'ab' => 'b');
$arr2 = array('0' => 'c', 'ab' => 'd');
var_dump($arr1+$arr2);
//输出
array(2) {
[0]=>
string(1) "a"
["ab"]=>
string(1) "b"
}
var_dump(array_merge($arr1, $arr2));
array(3) {
[0]=>
string(1) "a"
["ab"]=>
string(1) "d"
[1]=>
string(1) "c"
}

 

 

[转载]PHP反射(1)

PHP反射

注:文章转载自http://www.laravel-vue.xyz

反射介绍

PHP 反射机制,对类、接口、函数、方法和扩展进行反向工程的能力。
分析类,接口,函数和方法的内部结构,方法和函数的参数,以及类的属性和方法。

反射中常用的几个类:

  • ReflectionClass 解析类
  • ReflectionProperty 类的属性的相关信息
  • ReflectionMethod 类方法的有关信息
  • ReflectionParameter 取回了函数或方法参数的相关信息
  • ReflectionFunction 一个函数的相关信息

分析类:

class Student
{
    public $id;
    
    public $name;

    const MAX_AGE = 200;

    public static $likes = [];

    public function __construct($id, $name = 'li')
    {
        $this->id = $id;
        $this->name = $name;
    }

    public function study()
    {
        echo 'learning...';
    }

    private function _foo()
    {
        echo 'foo';
    }

    protected function bar($to, $from = 'zh')
    {
        echo 'bar';
    }
}

 

ReflectionClass

$ref = new ReflectionClass('Student');

// 判断类是否可实例化
if ($ref->isInstantiable()) {
    echo '可实例化';
}

// 获取类构造函数
// 有返回 ReflectionMethod 对象,没有返回 NULL
$constructor = $ref->getConstructor();
print_r($constructor);



// 获取某个属性
if ($ref->hasProperty('id')) {
    $attr = $ref->getProperty('id');
    print_r($attr);
}

// 获取属性列表
$attributes = $ref->getProperties();
foreach ($attributes as $row) {
    // 这里的 $row 为 ReflectionProperty 的实例
    echo $row->getName() , "\n";
}

// 获取静态属性,返回数组
$static = $ref->getStaticProperties();
print_r($static);

// 获取某个常量
if ($ref->hasConstant('MAX_AGE')) {
    $const = $ref->getConstant('MAX_AGE');
    echo $const;
}

// 获取常量,返回数组
$constants = $ref->getConstants();
print_r($constants);

// 获取某个方法
if ($ref->hasMethod('bar')) {
    $method = $ref->getMethod('bar');
    print_r($method);
}

// 获取方法列表
$methods = $ref->getMethods();
foreach ($methods as $key => $value) {
    // 这里的 $row 为 ReflectionMethod 的实例
    echo $value->getName() . "\n";
}

ReflectionProperty

if ($ref->hasProperty('name')) {
    $attr = $ref->getProperty('name');

    // 属性名称
    echo $attr->getName();

    // 类定义时属性为真,运行时添加的属性为假
    var_dump($attr->isDefault());

    // 判断属性访问权限
    var_dump($attr->isPrivate());
    var_dump($attr->isProtected());
    var_dump($attr->isPublic());
    // 判断属性是否为静态
    var_dump($attr->isStatic());
}

RefleactionMethod & ReflectionParameter

if ($ref->hasMethod('bar')) {
    $method = $ref->getMethod('bar');
    echo $method->getName();

    // isAbstract  判断是否是抽象方法
    //isConstructor  判断是否是构造方法
    //isDestructor  判断是否是析构方法
    //isFinal  判断是否是 final 描述的方法
    //isPrivate  判断是否是 private 描述的方法
    //isProtected 判断是否是 protected 描述的方法
    //isPublic 判断是否是 public 描述的方法
    //isStatic 判断是否是 static 描述的方法
    
    // 获取参数列表
    $parameters = $method->getParameters();
    foreach ($parameters as $row) {
        // 这里的 $row 为 ReflectionParameter 实例
        echo $row->getName();
        echo $row->getClass();

        // 检查变量是否有默认值
        if ($row->isDefaultValueAvailable()) {
            echo $row->getDefaultValue();
        }

        // 获取变量类型
        if ($row->hasType()) {
            echo $row->getType();
        }
        
    }
}

ReflectionFunction & ReflectionParameter

$fun = new ReflectionFunction('demo');
echo $fun->getName();
$parameters = $fun->getParameters();
foreach ($parameters as $row) {
    // 这里的 $row 为 ReflectionParameter 实例
    echo $row->getName();
    echo $row->getClass();

    // 检查变量是否有默认值
    if ($row->isDefaultValueAvailable()) {
        echo $row->getDefaultValue();
    }

    // 获取变量类型
    if ($row->hasType()) {
        echo $row->getType();
    }
}

综合实例

下面用一个简单的示例:如果用反射实例化类。
file: Student.php

class Student
{

    public $id;
    
    public $name;

    public function __construct($id, $name)
    {
        $this->id = $id;
        $this->name = $name;
    }

    public function study()
    {
        echo 'learning.....';
    }

}

一般情况下,实例化类的时候,直接使用 new,但是我们现在不用这种方法,我们使用反射来实现。
file: index.php

require 'student.php';

function make($class, $vars = [])
{
    $ref = new ReflectionClass($class);

    // 检查类 Student 是否可实例化
    if ($ref->isInstantiable()) {
        // 获取构造函数
        $constructor = $ref->getConstructor();
        // 没有构造函数的话,直接实例化
        if (is_null($constructor)) {
            return new $class;
        }
        // 获取构造函数参数
        $params = $constructor->getParameters();
        $resolveParams = [];
        foreach ($params as $key => $value) {
            $name = $value->getName();
            if (isset($vars[$name])) {
                // 判断如果是传递的参数,直接使用传递参数
                $resolveParams[] = $vars[$name];
            } else {
                // 没有传递参数的话,检查是否有默认值,没有默认值的话,按照类名进行递归解析
                $default = $value->isDefaultValueAvailable() ? $value->getDefaultValue() : null;
                if (is_null($default)) {
                    if ($value->getClass()) {
                        $resolveParams[] = make($value->getClass()->getName(), $vars);
                    } else {
                        throw new Exception("{$name} 没有传值且没有默认值");
                    }
                } else {
                    $resolveParams[] = $default;
                }
            }
        }

        // 根据参数实例化
        return $ref->newInstanceArgs($resolveParams);
    } else {
        throw new Exception("类 {$class} 不存在!");
    }
}

## 情况一
try {
    $stu = make('Student', ['id' => 1]);
    print_r($stu);
    $stu->study();
} catch (Exception $e) {
    echo $e->getMessage();
}

## 情况二
try {
    $stu = make('Student', ['id' => 1, 'name' => 'li']);
    print_r($stu);
    $stu->study();
} catch (Exception $e) {
    echo $e->getMessage();
}

上面两种情况很明显第一种,缺少参数 name,无法实例化成功,第二种情况就可以实例化成功。

那么我们如果将类 Student 的构造函数修改为:

public function __construct($id, $name = 'zhang')
{
    $this->id = $id;
    $this->name = $name;
}

这样设置 name 有默认值的情况下,那么第一种情况也可以实例化成功。

情况三

第三种情况:如果在类的构造函数中有其他类为参数的情况下,那么也可以解析:

public function __construct($id, $name, Study $study)
{
    $this->id = $id;
    $this->name = $name;
    $this->study = $study;
}

那么这种情况下,在分析类的构造函数参数的时候,如果没有传递参数的话,就会递归调用 make 方法处理 Study 类,如果类存在的话,实例化。

file: study.php

// 我们这里不写构造函数,测试下没有构造函数的情况
class Study
{
    public function show()
    {
        echo 'show';
    }
}

将 Student 类的方法 study 修改为:

1
2
3
4
public function study()
{
    $this->name . ' ' . $this->study->show();
}

下面测试:

try {
    $stu = make('Student', ['id' => 1]);
    print_r($stu);
    $stu->study();
} catch (Exception $e) {
    echo $e->getMessage();
}

PHP 的反射是一个很用的功能,我这里只能很简单的讲解了一点皮毛,详细介绍和用法可参看 官方手册