首页 > 技术文章 > PHP学习记录8

shawCE 2021-10-19 15:16 原文

PHP 7 新特性

PHP 7+ 版本新加特性如下表所示:

序号内容
1 PHP 标量类型与返回值类型声明
2 PHP NULL 合并运算符
3 PHP 太空船运算符(组合比较符)
4 PHP 常量数组
5 PHP 匿名类
6 PHP Closure::call()
7 PHP 过滤 unserialize()
8 PHP IntlChar()
9 PHP CSPRNG
10 PHP 7 异常
11 PHP 7 use 语句
12 PHP 7 错误处理
13 PHP intdiv() 函数
14 PHP 7 Session 选项
15 PHP 7 废弃特性
16 PHP 7 移除的扩展
17 PHP 7 移除的 SAPI

 

1.PHP 标量类型与返回值类型声明


标量类型声明

默认情况下,所有的PHP文件都处于弱类型校验模式。

PHP 7 增加了标量类型声明的特性,标量类型声明有两种模式:

  • 强制模式 (默认)
  • 严格模式

标量类型声明语法格式:

declare(strict_types=1);

代码中通过指定 strict_types的值(1或者0),1表示严格类型校验模式,作用于函数调用和返回语句;0表示弱类型校验模式。

可以使用的类型参数有:

  • int

  • float

  • bool

  • string

  • interfaces

  • array

  • callable

强制模式实例

实例

<?php
// 强制模式
function sum(int ...$ints)
{
   return array_sum($ints);
}

print(sum(2'3'4.1));
?>

以上程序执行输出结果为:

9

实例汇总将参数 4.1 转换为整数 4 后再相加。

严格模式实例

实例

<?php
// 严格模式
declare(strict_types=1);

function sum(int ...$ints)
{
   return array_sum($ints);
}

print(sum(2'3'4.1));
?>

以上程序由于采用了严格模式,所以如果参数中出现不适整数的类型会报错,执行输出结果为:

PHP Fatal error:  Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given, called in……

返回类型声明

PHP 7 增加了对返回类型声明的支持,返回类型声明指明了函数返回值的类型。

可以声明的返回类型有:

  • int

  • float

  • bool

  • string

  • interfaces

  • array

  • callable

返回类型声明实例

实例中,要求返回结果为整数:

实例

<?php
declare(strict_types=1);

function returnIntValue(int $value): int
{
   return $value;
}

print(returnIntValue(5));
?>

以上程序执行输出结果为:

5

返回类型声明错误实例

实例

<?php
declare(strict_types=1);

function returnIntValue(int $value): int
{
   return $value 1.0;
}

print(returnIntValue(5));
?>

以上程序由于采用了严格模式,返回值必须是 int,但是计算结果是float,所以会报错,执行输出结果为:

Fatal error: Uncaught TypeError: Return value of returnIntValue() must be of the type integer, float returned...

void 函数

一个新的返回值类型void被引入。 返回值声明为 void 类型的方法要么干脆省去 return 语句,要么使用一个空的 return 语句。 对于 void 函数来说,NULL 不是一个合法的返回值。

返回的类型还有 void,定义返回类型为 void 的函数不能有返回值,即使返回 null 也不行。

void 函数可以省去 return 语句,或者使用一个空的 return 语句。

实例

<?php
function swap(&$left, &$right) : void
{
    if ($left === $right) {
        return;
    }

    $tmp = $left;
    $left = $right;
    $right = $tmp;
}

$a = 1;
$b = 2;
var_dump(swap($a, $b), $a, $b);

以上实例输出结果:

null
int(2)
int(1)

对于标量类型声明:在严格模式下,有一种例外的情况是:当函数参数为float时,传入int型变量不会跑出typeerror,而是正常执行,在返回类型声明中,也是同样的:

<?php
declare(strict_types = 1);
function test (float $inter) {
    return $inter;
}

echo test(2); // 结果为2

function test1(int $inte) : float{
    return $inte;
}
echo test1(1); // 结果为1
?>
 
 

2.PHP NULL 合并运算符

PHP 7 新增加的 NULL 合并运算符(??)是用于执行isset()检测的三元运算的快捷方式。

NULL 合并运算符会判断变量是否存在且值不为NULL,如果是,它就会返回自身的值,否则返回它的第二个操作数。

以前我们这样写三元运算符:

$site = isset($_GET['site']) ? $_GET['site'] : '菜鸟教程';

现在我们可以直接这样写:

$site = $_GET['site'] ?? '菜鸟教程';

实例

<?php
// 获取 $_GET['site'] 的值,如果不存在返回 '菜鸟教程'
$site = $_GET['site'] ?? '菜鸟教程';

print($site);
print(PHP_EOL); // PHP_EOL 为换行符


// 以上代码等价于
$site = isset($_GET['site']) ? $_GET['site'] : '菜鸟教程';

print($site);
print(PHP_EOL);
// ?? 链
$site = $_GET['site'] ?? $_POST['site'] ?? '菜鸟教程';

print($site);
?>

以上程序执行输出结果为:

菜鸟教程
菜鸟教程
菜鸟教程

 

3.PHP 太空船运算符(组合比较符)

PHP 7 新增加的太空船运算符(组合比较符)用于比较两个表达式 $a$b,如果 $a 小于、等于或大于 $b时,它分别返回-1、0或1。

实例

<?php
// 整型比较
print( 1 <=> 1);print(PHP_EOL);
print( 1 <=> 2);print(PHP_EOL);
print( 2 <=> 1);print(PHP_EOL);
print(PHP_EOL); // PHP_EOL 为换行符

// 浮点型比较
print( 1.5 <=> 1.5);print(PHP_EOL);
print( 1.5 <=> 2.5);print(PHP_EOL);
print( 2.5 <=> 1.5);print(PHP_EOL);
print(PHP_EOL);

// 字符串比较
print( "a" <=> "a");print(PHP_EOL);
print( "a" <=> "b");print(PHP_EOL);
print( "b" <=> "a");print(PHP_EOL);
?>

以上程序执行输出结果为:

0
-1
1

0
-1
1

0
-1
1

 

4.PHP 常量数组

在 PHP 5.6 中仅能通过 const 定义常量数组,PHP 7 可以通过 define() 来定义。

实例

<?php
// 使用 define 函数来定义数组
define('sites', [
   'Google',
   'Runoob',
   'Taobao'
]);

print(sites[1]);
?>

以上程序执行输出结果为:

Runoob

 

5.PHP 匿名类

PHP 7 支持通过 new class 来实例化一个匿名类,这可以用来替代一些"用后即焚"的完整类定义。

实例

实例

<?php
interface Logger {
   public function log(string $msg);
}

class Application {
   private $logger;

   public function getLogger(): Logger {
      return $this->logger;
   }

   public function setLogger(Logger $logger) {
      $this->logger $logger;
   }  
}

$app = new Application;
// 使用 new class 创建匿名类
$app->setLogger(new class implements Logger {
   public function log(string $msg) {
      print($msg);
   }
});

$app->getLogger()->log("我的第一条日志");
?>

以上程序执行输出结果为:

我的第一条日志

 

6.PHP Closure::call()

PHP 7 的 Closure::call() 有着更好的性能,将一个闭包函数动态绑定到一个新的对象实例并调用执行该函数。

先看闭包函数:

PHP匿名函数(闭包函数)

匿名函数(Anonymous functions)就是没有函数名的函数,也叫闭包函数(closures),是在 php5.3 中新增一个特性。

PHP 允许临时创建一个没有指定名称的函数。匿名函数通常用在回调函数中,同时匿名函数也可以赋值给一个变量后使用,还能像其他任何 PHP 对象那样传递,不过匿名函数仍然是函数,因此可以调用,并且可以传入参数。

注意:理论上讲,闭包和匿名函数是不同的概念,不过 PHP 将其视作相同的概念,所以提到闭包时,指的就是匿名函数,反之亦然。

创建匿名函数

创建匿名函数很简单,其语法结构如下所示:

function (参数列表) {
    ...
}

可以看出,匿名函数与普通函数非常类似,同样可以接受参数,并且可以有返回值,只是匿名函数在声明时会省略函数名。

【示例】下面通过一个简单的示例来演示一下如何创建匿名函数,代码如下所示:
  1. <?php
  2. $url = function (){
  3. return 'http://c.biancheng.net/php/';
  4. };
  5. echo $url();
  6. ?>
运行结果如下:

http://c.biancheng.net/php/

通过上面的示例可以看出,匿名函数可以作为变量的值来使用。此时 PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。把一个 closure 对象赋值给一个变量的方式与普通变量赋值的语法是一样的,最后也要加上分号;

实际开发中,我们通常将匿名函数当做函数或方法的回调使用,在很多 PHP 内置函数中都会用到匿名函数,比如 array_map 和 preg_replace_callback,示例代码如下:
  1. <?php
  2. $arr = [1,2,3,4,5,6];
  3. $result = array_map(function ($num) {
  4. return $num*$num;
  5. }, $arr);
  6. echo '<pre>';
  7. print_r($result);
  8. ?>
运行结果如下:

Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => 16
    [4] => 25
    [5] => 36
)

再来学一下array_map preg_replace_callback

 

array_map

(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)

array_map — 为数组的每个元素应用回调函数

 

说明

array_map(callable $callback, array $array, array ...$arrays): array

array_map():返回数组,是为 array 每个元素应用 callback函数之后的数组。 array_map() 返回一个 array,数组内容为 array1 的元素按索引顺序为参数调用 callback 后的结果(有更多数组时,还会传入 arrays 的元素)。 callback 函数形参的数量必须匹配 array_map() 实参中数组的数量。

 

参数

 callback

回调函数 callable,应用到每个数组里的每个元素。

多个数组操作合并时,callback 可以设置为 null。 如果只提供了 array 一个数组, array_map() 会返回输入的数组。

array

数组,遍历运行 callback 函数。

arrays

额外的数组列表,每个都遍历运行 callback 函数。

 

返回值

返回数组,包含 callback 函数处理之后 array (有多个数组时,为 arrays) 对应索引所有元素作为函数的参数。

当仅仅传入一个数组时,返回的数组会保留传入参数的键(key)。 传入多个数组时,返回的数组键是按顺序的 integer。

 

范例

 示例 #1 array_map() 例子

<?php
function cube($n)
{
    return ($n $n $n);
}

$a = [12345];
$b array_map('cube'$a);
print_r($b);
?>

这使得 $b 成为:

Array
(
    [0] => 1
    [1] => 8
    [2] => 27
    [3] => 64
    [4] => 125
)

 

示例 #2 array_map() 使用匿名函数 (PHP 5.3.0 起)

<?php
$func = function($value) {
    return $value 2;
};

print_r(array_map($funcrange(15)));
?>
Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 8
    [4] => 10
)

 

示例 #3 array_map():使用更多的数组

<?php
function show_Spanish($n$m)
{
    return "The number {$n} is called {$m} in Spanish";
}

function map_Spanish($n$m)
{
    return [$n => $m];
}

$a = [12345];
$b = ['uno''dos''tres''cuatro''cinco'];

$c array_map('show_Spanish'$a$b);
print_r($c);

$d array_map('map_Spanish'$a $b);
print_r($d);
?>

以上例程会输出:

// printout of $c
Array
(
    [0] => The number 1 is called uno in Spanish
    [1] => The number 2 is called dos in Spanish
    [2] => The number 3 is called tres in Spanish
    [3] => The number 4 is called cuatro in Spanish
    [4] => The number 5 is called cinco in Spanish
)

// printout of $d
Array
(
    [0] => Array
        (
            [1] => uno
        )

    [1] => Array
        (
            [2] => dos
        )

    [2] => Array
        (
            [3] => tres
        )

    [3] => Array
        (
            [4] => cuatro
        )

    [4] => Array
        (
            [5] => cinco
        )

)

传入两个及以上的数组时,它们元素数量将会相同。因为回调函数会并行地处理相互对应的元素。 如果几个数组的元素数量不一致:空元素会扩展短那个数组,直到长度和最长的数组一样。

此函数有个有趣的用法:传入 null 作为回调函数的名称,将创建多维数组(一个数组,内部包含数组。)

 

示例 #4 多个数组的合并操作

<?php
$a = [12345];
$b = ['one''two''three''four''five'];
$c = ['uno''dos''tres''cuatro''cinco'];

$d array_map(null$a$b$c);
print_r($d);
?>

以上例程会输出:

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => one
            [2] => uno
        )

    [1] => Array
        (
            [0] => 2
            [1] => two
            [2] => dos
        )

    [2] => Array
        (
            [0] => 3
            [1] => three
            [2] => tres
        )

    [3] => Array
        (
            [0] => 4
            [1] => four
            [2] => cuatro
        )

    [4] => Array
        (
            [0] => 5
            [1] => five
            [2] => cinco
        )

)

 

示例 #5 仅有 array1 时,callback 设置为 null

<?php
$array = [123];
var_dump(array_map(null$array));
?>

以上例程会输出:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

 

示例 #6 array_map() 键(key)是 string

<?php
$arr = array("stringkey" => "value");
function cb1($a) {
    return [$a];
}
function cb2($a$b) {
    return [$a$b];
}
var_dump(array_map('cb1'$arr));
var_dump(array_map('cb2'$arr$arr));
var_dump(array_map(null,  $arr));
var_dump(array_map(null$arr$arr));
?>

以上例程会输出:

array(1) {
  ["stringkey"]=>
  array(1) {
    [0]=>
    string(5) "value"
  }
}
array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(5) "value"
    [1]=>
    string(5) "value"
  }
}
array(1) {
  ["stringkey"]=>
  string(5) "value"
}
array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(5) "value"
    [1]=>
    string(5) "value"
  }
}

 

preg_replace_callback

preg_replace_callback — 执行一个正则表达式搜索并且使用一个回调进行替换。(通过正则表达式找出要修改的字符串片段,使用回调函数对片段进行修改后并替换原片段,返整个字符串)

 

<?php
// 将文本中的年份增加一年.
$text = "April fools day is 04/01/2002\n";
$text.= "Last christmas was 12/24/2001\n";
// 回调函数
function next_year($matches)
{
    // 通常: $matches[0]是完成的匹配
    // $matches[1]是第一个捕获子组的匹配
    // 以此类推
    /*
     * preg_replace_callback运行流程:首先对text进行正则表达式过滤获得若干个数组(正则表达式有几个匹配结果就有几个数组),循环执行每个数组
     *
     * 流程是:首先根据正则表达式获取text中日期字符串(匹配到几个就有多少个数组) 这里返回两串04/01/2002和12/24/2001
     * 依次将两串字符串输入matches中,即matches是一个数组,接受从text中结果正则表达式过滤后获得的字符串(会形成新的数组),
     * 此处由于"|(\d{2}/\d{2}/)(\d{4})|“表达式两个括号分两个部分,即月日一部分、年一部分,所以最终形成了一个数组:
     * array(3) { [0]=> string(10) "04/01/2002" [1]=> string(6) "04/01/" [2]=> string(4) "2002" }
     * 再执行$matches[2]+1 增加年份一年
     * 结束04/01/2002这个数组
     * 开始12/24/2001新数组  。。。
     * 完成所有数组后,即完成了text中所有通过正则表达式找出来的日期字符串的更新操作,已经对text做出了改动,
     * 最后preg_replace_callback返回了text
     *
     * 下面注释的是第一个数组的输出结果
     * */
    var_dump($matches);//array(3) { [0]=> string(10) "04/01/2002" [1]=> string(6) "04/01/" [2]=> string(4) "2002" }
    echo $matches[0];// 04/01/2002
    echo $matches[1];// 04/01/
    echo $matches[2];// 2002
    return $matches[1].($matches[2]+1);//RETURN 结果是: 04/01/2003

}
echo preg_replace_callback(
    "|(\d{2}/\d{2}/)(\d{4})|",
    "next_year",
    $text);

?>

 

 

 拓展:

preg_replace

preg_replace — 执行一个正则表达式的搜索和替换

 preg_replace(

    string|array $pattern,
    string|array $replacement,
    string|array $subject,
    int $limit = -1,
    int &$count = null
): string|array|null
搜索 subject 中匹配 pattern 的部分,以 replacement 进行替换。

返回值

如果 subject 是一个数组,preg_replace() 返回一个数组,其他情况下返回一个字符串。

如果匹配被查找到,替换后的 subject 被返回,其他情况下返回没有改变的 subject。如果发生错误,返回 null

preg_replace() 中使用基于索引的数组

<?php
$string 'The quick brown fox jumps over the lazy dog.';
$patterns = array();
$patterns[0] = '/quick/';
$patterns[1] = '/brown/';
$patterns[2] = '/fox/';
$replacements = array();
$replacements[2] = 'bear';
$replacements[1] = 'black';
$replacements[0] = 'slow';
echo preg_replace($patterns$replacements$string);
?>

以上例程会输出:

The bear black slow jumps over the lazy dog.

对模式和替换内容按 key 进行排序我们可以得到期望的结果。

<?php
ksort($patterns);
ksort($replacements);
echo preg_replace($patterns$replacements$string);
?>

以上例程会输出:

The slow black bear jumps over the lazy dog.

 

示例 #3 替换一些值

<?php
$patterns = array ('/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/',
                   '/^\s*{(\w+)}\s*=/');
$replace = array ('\3/\4/\1\2''$\1 =');
echo preg_replace($patterns$replace'{startDate} = 1999-5-27');
?>

以上例程会输出:

$startDate = 5/27/1999

 

 示例 #4 剥离空白字符

这个例子剥离多余的空白字符

<?php
$str 'foo   o';
$str preg_replace('/\s\s+/'' '$str);
// 将会改变为'foo o'
echo $str;
?>
preg_replace() 中使用基于索引的数组
<?php
$string 'The quick brown fox jumps over the lazy dog.';
$patterns = array();
$patterns[0] = '/quick/';
$patterns[1] = '/brown/';
$patterns[2] = '/fox/';
$replacements = array();
$replacements[2] = 'bear';
$replacements[1] = 'black';
$replacements[0] = 'slow';
echo preg_replace($patterns$replacements$string);
?>

以上例程会输出:


The bear black slow jumps over the lazy dog.

对模式和替换内容按 key 进行排序我们可以得到期望的结果。


<?php
ksort($patterns);
ksort($replacements);
echo preg_replace($patterns$replacements$string);
?>

以上例程会输出:


The slow black bear jumps over the lazy dog.

 示例 #3 替换一些值

<?php
$patterns = array ('/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/',
                   '/^\s*{(\w+)}\s*=/');
$replace = array ('\3/\4/\1\2''$\1 =');
echo preg_replace($patterns$replace'{startDate} = 1999-5-27');
?>

以上例程会输出:

$startDate = 5/27/1999

 示例 #4 剥离空白字符

这个例子剥离多余的空白字符

<?php
$str 'foo   o';
$str preg_replace('/\s\s+/'' '$str);
// 将会改变为'foo o'
echo $str;
?>

注意:

当使用数组形式的patternreplacement时, 将会按照key在数组中出现的顺序进行处理. 这不一定和数组的索引顺序一致. 如果你期望使用索引对等方式用replacementpattern 进行替换, 你可以在调用preg_replace()之前对两个数组各进行一次ksort()排序.

 

use 关键字

使用 use 关键字,闭包函数可以实现从父级作用域中继承变量,但是从 php7.1 开始,不支持继承预定义变量和 $this。

【示例】下面通过示例来演示 use 关键字的使用,代码如下所示:
  1. <?php
  2. function demo() {
  3. $website = 'C语言中文网<br>';
  4. $url = 'http://c.biancheng.net/php/';
  5. $func = function() use ($website) {
  6. echo '$website = '.$website;
  7. echo '$url = '.$url;
  8. };
  9. $func();
  10. }
  11. demo();
  12. ?>
运行结果如下:

$website = C语言中文网
$url =

通过运行结果可以看出,$url 并没有通过 use 关键字继承到匿名函数中,所以无法打印 $url 的值。

需要注意的是,匿名函数虽然可以继承父级作用域中的变量,但是在匿名函数中修改变量的值不会对父级作用域中的变量造成影响,示例代码如下:
  1. <?php
  2. function demo() {
  3. $num = 1;
  4. $func = function() use ($num) {
  5. $num++;
  6. echo '匿名函数中 $num 的值为:'.$num.'<br>';
  7. };
  8. $func();
  9. echo '匿名函数外 $num 的值为:'.$num;
  10. }
  11. demo();
  12. ?>
运行结果如下:

匿名函数中 $num 的值为:2
匿名函数外 $num 的值为:1

如果想要在修改匿名函数继承的变量的同时,同样修改其父级作用域中的变量,则需要在变量名的前面添加 & 符号,类似于函数中的引用传递。示例代码如下:
  1. <?php
  2. function demo() {
  3. $num = 1;
  4. $func = function() use (&$num) {
  5. $num++;
  6. echo '匿名函数中 $num 的值为:'.$num.'<br>';
  7. };
  8. $func();
  9. echo '匿名函数外 $num 的值为:'.$num;
  10. }
  11. demo();
  12. ?>
运行结果如下:

匿名函数中 $num 的值为:2
匿名函数外 $num 的值为:2

 

实例

实例

<?php
class {
    private $x 1;
}

// PHP 7 之前版本定义闭包函数代码
$getXCB = function() {
    return $this->x;
};

// 闭包函数绑定到类 A 上
$getX $getXCB->bindTo(new A'A'); 

echo $getX();
print(PHP_EOL);

// PHP 7+ 代码
$getX = function() {
    return $this->x;
};
echo $getX->call(new A);
?>

以上程序执行输出结果为:

1
1

 

PHP闭包之bind和bindTo

Closure类摘要如下:

Closure {  
    __construct ( void )  
    public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static'] 
    public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}

方法说明:

Closure::__construct — 用于禁止实例化的构造函数
Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。
Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。

闭包之bind方法

一个实例

<?php
/**
 * 复制一个闭包,绑定指定的$this对象和类作用域。
 *
 * @author
 */
class Animal {
    private static $cat = "cat";
    private $dog = "dog";
    public $pig = "pig";
}

/*
 * 获取Animal类静态私有成员属性
 */
$cat = static function() {
    return Animal::$cat;
};

/*
 * 获取Animal实例私有成员属性
 */
$dog = function() {
    return $this->dog;
};

/*
 * 获取Animal实例公有成员属性
 */
$pig = function() {
    return $this->pig;
};

$bindCat = Closure::bind($cat, null, new Animal());// 给闭包绑定了Animal实例的作用域,但未给闭包绑定$this对象
$bindDog = Closure::bind($dog, new Animal(), 'Animal');// 给闭包绑定了Animal类的作用域,同时将Animal实例对象作为$this对象绑定给闭包
$bindPig = Closure::bind($pig, new Animal());// 将Animal实例对象作为$this对象绑定给闭包,保留闭包原有作用域
echo $bindCat(),'<br>';// 根据绑定规则,允许闭包通过作用域限定操作符获取Animal类静态私有成员属性
echo $bindDog(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象(Animal实例对象)获取Animal实例私有成员属性
echo $bindPig(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象获取Animal实例公有成员属性<?php
总结:
  • bind函数:
    • 参数1($closure) : 表示闭包函数
    • 参数2($newthis): 相当于在函数内/外调用的区别,传类的实例表示在内部调用,NULL相当于在外部调用
    • 参数3($newscope): 相当于类和实例调用的区别,函数的作用域, 传类表示静态调用方式,内部可以“类名::属性”的方式使用;实例表示实例调用方式,内部可以“->”
 

  从手册知道,Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。

  创建并返回一个 匿名函数, 它与当前对象的函数体相同、绑定了同样变量,但可以绑定不同的对象,也可以绑定新的类作用域。

<?php

class A
{

    function __construct($val)
    {
        $this->val = $val;
    }

    function getClosure()
    {
        return function(){return $this->val;};
    }
}

$ob1 = new A(1);
$cl = $ob1->getClosure();
echo $cl(),"<br/>";


$ob2 = new A(2);
$cl = $cl->bindTo($ob2); //闭包指定作用域在$ob2对象上。
echo $cl()."<br/>"; //2

 

 Closure闭包中的bind与bindTo方法的区别

 Closure是PHP里的一个final类,其有两个方法(bind和bindTo),在定义一个闭包函数时实际就是定义了一个Closure的实例化对象。所有所有的闭包函数都自带有bind和bindTo方法:

•Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。
•Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。

    看手册上的描述,很难找到bind和bindTo之间的区别,而事实也的确如此,这两个方法除了在调用方式方法(一个使用::调用,一个使用->调用)不同外,生成的闭包是一样的。相比较而言,bindTo方法是一个更简单的写法。例如我们已经定义了一个闭包$closure,此时我们要得到一个绑定类后的闭包对象,我们有以下三种写法:

//匿名函数绑定到类的写法
$newClosure = Closure::bind($closure, new A(), 'A');
$newClosure = $closure::bind($closure, new A(), 'A');
$newClosure = $closure->bindTo(new A(), 'A');

    因为闭包函数$closure本身就是一个Closure,$closure也有bind方法,所有第一种、第二种是一样的,而我们从第二种写法可明显看到重复了一个$closure,这完全没有必要,既然是$closure发起调用bind方法,没必要再将自己做为参数传入,所以第三种写法从上到下可以看出是一种更优的调用方法。

    在bind和bindTo的使用上,两者并没有多大差别,如下示例:

//闭包函数Closure中对静态与动态属性的调用
Class Student
{
    static $name = 'kermit';
    private $age = 20;
}

//实例化类,定义匿名函数
$student  = new Student();
$getName = function()
{
    return self::$name .'--'.$this->age;
};

//将匿名函数绑定到类:
$getNameClosure = $getName::bind($getName, $student, 'Student');
echo $getNameClosure();
echo '<br>';

$otherClosure = $getName->bindTo($student, 'Student');
echo $otherClosure();

     包括我对各个步骤进行了var_dump打印查看,两个方法生成的闭包对象功能都一样。另外在一系列使用之后,在bind和bindTo的传参上的一些个人总结如下:

1,bind和bindTo的类参数(即上例中的$student)是一个必传参数,如果传null。则闭包函数中不能使用$this,如果传了一个实例化类,闭包函数中才能使用$this。

2,bind和bindTo的类作用域参数(即上例中的'Student'参数)默认为Static,如果使用默认值,则闭包函数中不能使用self调用,如果传了类名,则可以使用self.

3,如果闭包函数存在调用类的private或protected属性或方法,则必须传递第三个参数指明类作用域才能正常调用。

 

 

7.PHP 过滤 unserialize()

PHP 7 增加了可以为 unserialize() 提供过滤的特性,可以防止非法数据进行代码注入,提供了更安全的反序列化数据。

实例

实例

<?php
class MyClass1 
   public $obj1prop;   
}
class MyClass2 {
   public $obj2prop;
}


$obj1 = new MyClass1();
$obj1->obj1prop 1;
$obj2 = new MyClass2();
$obj2->obj2prop 2;

$serializedObj1 serialize($obj1);
$serializedObj2 serialize($obj2);

// 默认行为是接收所有类
// 第二个参数可以忽略
// 如果 allowed_classes 设置为 false, unserialize 会将所有对象转换为 __PHP_Incomplete_Class 对象
$data unserialize($serializedObj1 , ["allowed_classes" => true]);

// 转换所有对象到 __PHP_Incomplete_Class 对象,只允许 MyClass1 和 MyClass2 转换到 __PHP_Incomplete_Class
$data2 unserialize($serializedObj2 , ["allowed_classes" => ["MyClass1""MyClass2"]]);

print($data->obj1prop);
print(PHP_EOL);
print($data2->obj2prop);
?>

以上程序执行输出结果为:

1
2

PHP unserialize() 函数

unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。

PHP 版本要求: PHP 4, PHP 5, PHP 7

语法

mixed unserialize ( string $str )

参数说明:

  • $str: 序列化后的字符串。

返回值

返回的是转换之后的值,可为 integer、float、string、array 或 object。

如果传递的字符串不可解序列化,则返回 FALSE,并产生一个 E_NOTICE。

实例

实例

<?php $str = 'a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}'; $unserialized_data = unserialize($str); print_r($unserialized_data); ?>

输出结果为:

Array
(
    [0] => Google
    [1] => Runoob
    [2] => Facebook
)

 

PHP serialize() 函数

serialize() 函数用于序列化对象或数组,并返回一个字符串。

serialize() 函数序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结构不会改变。

如果想要将已序列化的字符串变回 PHP 的值,可使用 unserialize()

PHP 版本要求: PHP 4, PHP 5, PHP 7

语法

string serialize ( mixed $value )

参数说明:

  • $value: 要序列化的对象或数组。

返回值

返回一个字符串。

实例

实例

<?php $sites = array('Google', 'Runoob', 'Facebook'); $serialized_data = serialize($sites); echo $serialized_data . PHP_EOL; ?>

输出结果为:

a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}

 

 

推荐阅读