php的匿名函数

1
2
3
4
5
匿名函数
匿名函数(Anonymous functions),也叫闭包函数(closures),
允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。
当然,也有其它应用的情况。

闭包的写法

5.2本版以下

看起来好怪异

<?php
$newfunc  =  create_function('$a, $b',
  'return "ln($a) + ln($b) = " . log($a * $b);' 
);
echo  "New anonymous function:  $newfunc \n" ;
echo  $newfunc ( 2 ,  M_E ) .  "\n" ;
 // outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599
?> 

##5.3版本以上
和js很相像。

<?php
$greet  = function( $name )
{
     printf ( "Hello %s\r\n" ,  $name );
};

 $greet ( 'World' );
 $greet ( 'PHP' );

打印$greet变量结果一下

object(Closure)#1 (1) {
  ["parameter"]=>
  array(1) {
    ["$name"]=>
    string(10) "<required>"
  }
}

可以看出$greet其实一个Closure对象实例。
$greet()其实执行的是魔术方法__invoke()

代码如下:

<?php

/**
 * 
 */
class Obj
{
    /**
     * [__invoke description]
     * @param  [type] $name [description]
     * @return [type]       [description]
     */
    public function __invoke($name)
    {
        printf ( "Hello %s\r\n" ,  $name );
    }

    /**
     * [test description]
     * @return [type] [description]
     */
    public function test()
    {
        return function($name){
            printf ( "Hello %s\r\n" ,  $name );
        };
    }
}

$greet = new Obj();
$greet("World"); //Hello World
$hello = $greet->test();
$hello('zhang san'); //Hello zhang san

##作用域
匿名函数闭包使用use关键字使用外部变量。

<?php
class Obj
{
    public function hello()
    {
        $name = 'zhangsan';
        $greet = function($prefix) use ($name) {
            echo "{$prefix}, {$name}";
        };

        $greet('hi');
    }
}


$obj = new Obj();
$obj->hello(); //hi, zhangsan

$this是一个特殊变量不能直接使用(作用域的原因参考js的this 的工作原理)。可以使用如下代码:

<?php
class Obj
{
    public $name = 'zhangsan';

    public function hello()
    {
        $that = $this;
        $greet = function($prefix) use ($that) {
            echo "{$prefix}, {$this->name}";
        };

        $greet('hi');
    }
}


$obj = new Obj();
$obj->hello(); //hi, zhangsan

##绑定函数到一个对象
绑定使用的是Closure::bind/bindTo — 复制一个闭包,绑定指定的$this对象和类作用域。

<?php
$f = function()
{
    return $this->value + 2;
};

class NewValueHolder
{
    public $value;
}

$vh = new NewValueHolder();
$vh->value = 3;
$c = $f->bindTo($vh);
var_dump($c()); //Fatal error: Cannot access protected property NewValueHolder::$value

上述代码会报错是因为$this的作用并没有发生变化。

1
2
3
4
public Closure Closure::bindTo ( object $newthis [, mixed $newscope = 'static' ] )
newscope
关联到匿名函数的类作用域,或者 'static' 保持当前状态。如果是一个对象,则使用这个对象的类型为心得类作用域。 这会决定绑定的对象的 保护、私有成员 方法的可见性。

修改bindTo为$c = $f->bindTo($vh, $vh);则可以按照预期运行。

带你穿越带你飞~,一秒钟PHP变JS =^ =!!!这篇文章有更淫荡的用法。

闭包的应用

  1. 作为回调函数(array_map, array_reduce,array_walk等)

    <?php
    $data [] = array( 'volume'  =>  67 ,  'edition'  =>  2 );
    $data [] = array( 'volume'  =>  86 ,  'edition'  =>  1 );
    $data [] = array( 'volume'  =>  85 ,  'edition'  =>  6 );
    $data [] = array( 'volume'  =>  98 ,  'edition'  =>  2 );
    $data [] = array( 'volume'  =>  86 ,  'edition'  =>  6 );
    $data [] = array( 'volume'  =>  67 ,  'edition'  =>  7 );
    
    $volumes = array_map(function($row){
        return $row['volume'];
    }, $data);
    
$volumeSum = 0;
$volumeSum = array_reduce($volumes, function($volumeSum, $current){
    $volumeSum += $current;
    return $volumeSum;
});

print_r($volumes); //volume列的所有值

print_r($volumeSum); //volume列的总和
  1. 复用函数或方法的代码片段

    <?php
    
    /**
     * 
     */
    class Obj
    {
        public function calc($num)
        {
            $space = function($num) {
                return $num + 1;
            };
    
            if(逻辑1) {
                $num1 = $space($num);
            } elseif(逻辑2) {
                $num2 = $space($num2);
            }
        }
    }
    

参考

Closure 类

匿名函数

$this in PHP closures

实现远程文件打包下载

  • 传统实现方法
    下载远程文件到本地服务器—>压缩–>下载–>删除。
    这样实现 消耗内存、cpu、大量磁盘空间、速度太慢(取决于网络).

  • 使用第三方mod_zip 实现打包下载。
    内存消耗极低。
    通过输出X-Archive-Files和文件列表,自动将远程文件打包下载避免了临时文件的创建和删除。

安装过程

下载mod_zip 源文件

1
2
#cd /usr/local/src
#git clone https://github.com/evanmiller/mod_zip.git

tengine安装过程

使用dso-tool编译动态模块

1
2
3
/usr/local/tengine/bin/dso-tool \
--add-module=/usr/local/src/mod_zip/ \
--dst=/usr/local/tengine/modules/

报错1

此处输入图片的描述

1
#vi +331 /usr/local/src/mod_zip/ngx_http_zip_file.c

修改为
此处输入图片的描述
保存
重新编译
仍然报错

ngx_http_zip_header_charset_name defined but not use```
1
2
```
#vi +11 /usr/local/src/mod_zip/ngx_http_zip_file.c

注释掉

1
//static ngx_str_t ngx_http_zip_header_charset_name = ngx_string("upstream_http_x_archive_charset");

重新编译通过

查看动态模块是否生成

1
#ls /usr/local/tengine/modules/

  • 修改nginx配置文件测试
1
2
3
4
5
6
7
#vi nginx.conf
dso {
...
load ngx_http_zip_module.so;
...
}
  • 编写测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vi zip.php
<?php
header('X-Accel-Chareset: utf-8');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=test.zip');
header('X-Archive-Files: zip');
$crc32 = "-";
//格式 crc32(可以用-忽略)、文件大小、文件uri、文件名
printf("%s %d %s %s\n", $crc32, 688746, '/lms/M00/00/11/qMB6HVOaakWARZXJAAqCatIt43Y37.pptx', '1.pptx');
printf("%s %d %s %s\n", $crc32, 5236, '/default.conf', 'd.conf');
?>
  • tips

    • 文件大小一定要正确 否则压缩打不开
    • 文件uri (非url 远程文件需要配置nginx跳转).
      例子中的lms(fastdfs)远程文件 需要在nginx内做配置跳转到正确的地址。
    • 如果不需要断点续传crc32参数可以使用-忽略。
      1
      2
      3
      4
      5
      6
      7
      8
      #vi nginx.conf
      server {
      ...
      location /lms {
      proxy_pass http://fastdf_server;
      }
      ...
      }

nginx 安装

  • 查看旧的编译参数

    1
    #/usr/local/nginx/sbin/nginx -V
  • 编译

1
2
3
4
5
6
#/usr/local/nginx/configure \
--prefix=/usr/local/nginx \
--user=www --group=www \
--add-module=/usr/local/src/mod_zip
#make

报错级处理参考tengine内的处理方法.

#升级nginx

1
2
3
4
#cp objs/nginx /usr/local/nginx/sbin
#/usr/local/nginx/sbin/nginx -t
#/usr/local/nginx/sbin/nginx

参考文档
NgxZip
利用Nginx第三方模块,实现附件打包下载
mod_zip

pack函数的简单使用

pack函数主要作用是把数据压缩为二进制字符串。qq纯净ip数据库数据存储就是使用pack进行封装。本文为pack的简单应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
class Db {
private $file = null;
private $struct = array(
'name' => 'a8',
'age' => 'S',
'email' => 'a30'
);
private $length = 40;
private function init()
{
$file = fopen('./test.db', 'ab+');
$this->file = $file;
}
public function __construct()
{
if($this->file === null) {
$this->init();
}
}
public function add($data)
{
$binary = '';
foreach($data as $k => $row) {
$struct = $this->struct[$k];
$binary .= pack($struct, $row);
}
fwrite($this->file, $binary);
}
public function read($id = 1)
{
rewind($this->file);
$data = array();
if(!fseek($this->file, $this->length * ($id -1))) {
$data['name'] = unpack('a8', fread($this->file, 8))[1];
$data['age'] = unpack('S', fread($this->file, 2))[1];
$data['email'] = unpack('a30', fread($this->file, 30))[1];
}
return $data;
}
public function __destruct()
{
fclose($this->file);
}
}

存储数据

1
2
3
4
5
6
7
$data = array(
'name' => 'lisi' . time(),
'age' => 100,
'email' => 'jjjjj@qq.com'
);
$db = new Db();
$db->add($data);

读取数据

1
2
3
4
5
6
7
8
9
10
11
$data = $db->read(1);
print_r($data);
/*
Array
(
[name] => lisi1418
[age] => 100
[email] => jjjjj@qq.com
)
*/

不使用strrev和数组函数实现字符串反转

反转一个字符串很容易使用strrev函数即可,假如没有strrev函数也没有数组函数怎么显示字符串反转。

  • 背景知识

    1
    2
    string 中的字符可以通过一个从 0 开始的下标,用类似 array 结构中的方括号包含对应的数字来访问和修改,比如 $str[42]。
    可以把 string 当成字符组成的 array。函数 substr() 和 substr_replace() 可用于操作多于一个字符的情况。 -- php手册
  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <?php
    function reverseString($string)
    {
    $length = strlen($string);
    $reverseString = '';
    while($length--) {
    $reverseString .= $string[$length];
    }
    return $reverseString;
    }
    $str = "Hello world";
    echo reverseString($str); //dlrow olleH
    ```php
    使用str_split 函数也可以。
    ```php
    $array = str_split($str);
    print_r($array);
    /**
    Array
    (
    [0] => H
    [1] => e
    [2] => l
    [3] => l
    [4] => o
    [5] =>
    [6] => w
    [7] => o
    [8] => r
    [9] => l
    [10] => d
    )
    */

参考 字符串反转,神奇的算法-读《编程珠玑(第二版)》 有更高效的写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
function ReverseString(&$s, $from, $to)
{
while ($from < $to)
{
$t = $s[$from];
$s[$from++] = $s[$to];
$s[$to--] = $t;
}
}
function LeftRotateString($s, $n, $m)
{
$m %= $n; //若要左移动大于n位,那么和%n 是等价的
ReverseString($s, 0, $m - 1); //反转[0..m - 1],套用到上面举的例子中,就是X->X^T,即 abc->cba
ReverseString($s, $m, $n - 1); //反转[m..n - 1],例如Y->Y^T,即 def->fed
ReverseString($s, 0, $n - 1); //反转[0..n - 1],即如整个反转,(X^TY^T)^T=YX,即 cbafed->defabc。
return $s;
}
echo LeftRotateString('abcdef', 6, 3); //defabc

php获取视频文件编码信息

使用ffmpeg

1
2
FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能[1],
包含了libavcodec ─这是一个用于多个项目中音频和视频的解码器库,以及libavformat——一个音频与视频格式转换库。
  • 安装ffmpeg
1
2
3
4
5
6
#rpm -ivh http://apt.sw.be/redhat/el6/en/i386/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.i686.rpm
#yum install ffmpeg ffmpeg-devel
#ffmpeg -v
FFmpeg version 0.6.5, Copyright (c) 2000-2010 the FFmpeg developers
  • 使用命令行获取文件信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # ffmpeg -i 庞麦郎_-_我的滑板鞋.mp4
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '庞麦郎_-_我的滑板鞋.mp4':
    Metadata:
    major_brand : isom
    minor_version : 512
    compatible_brands: isomiso2avc1mp41
    encoder : Lavf54.6.100
    Duration: 00:03:36.48, start: 0.000000, bitrate: 2778 kb/s
    Stream #0.0(und): Video: h264, yuv420p, 1920x1080 [PAR 1:1 DAR 16:9], 2674 kb/s, 25 fps, 25 tbr, 25025 tbn, 50 tbc
    Stream #0.1(und): Audio: aac, 44100 Hz, stereo, s16, 97 kb/s
    At least one output file must be specified
  • 使用正则提取需要的信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
    function getVideoFormat($file)
    {
    $ffmpeg = "/usr/bin/ffmpeg";
    ob_start();
    passthru(sprintf($ffmpeg.' -i "%s" 2>&1', $file));
    $info = ob_get_contents();
    ob_end_clean();
    preg_match('/Video:\s(?<format>.*?),/', $info, $match);
    return $match;
    }
    $file = __DIR__ . '/庞麦郎_-_我的滑板鞋.mp4';
    var_dump(getVideoFormat($file));
    /*
    array(3) {
    [0]=>
    string(12) "Video: h264,"
    ["format"]=>
    string(4) "h264"
    [1]=>
    string(4) "h264"
    }
    */

使用getID3类库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$filename = __DIR__ .'/../庞麦郎_-_我的滑板鞋.mp4';
// Analyze file and store returned data in $ThisFileInfo
$ThisFileInfo = $getID3->analyze($filename);
print_r($ThisFileInfo['video']);
/*
Array
(
[dataformat] => quicktime
[resolution_x] => 1920
[resolution_y] => 1080
[fourcc] => avc1
[frame_rate] => 25
)
*/