PHP 使用pcntl和libevent 实现Timer功能,先看例子,pcntl(PHP线程)解释在下面。
<?php
function newChild($func_name) {
echo "enter newChild\n";
$args = func_get_args();
unset($args[0]);
$pid = pcntl_fork();
if ($pid == 0) {
function_exists($func_name) and exit(call_user_func_array($func_name, $args)) or exit(-1);
} else if($pid == -1) {
echo "Couldn't create child process";
} else {
return $pid;
}
}
(PS:^_^不错的php开发交流群:256271784,验证:csl,有兴趣的话可以加入进来一起讨论)
function on_timer() {
echo "timer called\n";
}
/**
* @param $func string, function name
* @param $timeouts int, microtimes for time delay
*/
function timer($func, $timeouts){
echo "enter timer\n";
$base = event_base_new();
$event = event_new();
event_set($event, 0, EV_TIMEOUT, $func);
event_base_set($event, $base);
event_add($event, $timeouts);
event_base_loop($base);
}
$pid = newChild("timer", "on_timer", 5000000);
if ($pid > 0) {
echo "master process exit\n";
}
PHP 扩展pcntl 实现 ” 多线程 ”( 进程 )
pcntl 与 ticks
ticks 是通过 declare(ticks = n) {statement} 语法定义的 , declare 语法目前只能接受 ticks, 他定义的 ticks = n 的意义是当 declare 指定的语句块中执行了 N 条低级语句去发生一个事件 , 这个事件可以通过 register_tick_function($function_name) 来注册 .
pcntl 的信号机制是基于 ticks 机制实现的 . 因此 , 我们使用 pcntl 族函数中信号相关的函数时 , 需要在前面增加 declare(ticks = n) 语法结构 .
int pcntl_alarm(int $seconds):
$seconds 秒后向进程发送一个 SIGALRM 信号 , 每次调用 pcntl_alarm 方法都会取消之前设置的时钟 .
void pcntl_exec(string $path[, array $args[, array $env]]):
在当前进程空间执行一个程序 .
$path: 必须是二进制可执行文件 , 或具有有效脚本头信息 (#!/usr/local/bin/php) 的脚本文件路径 .
$args: 将要传递给该程序的字符串参数列表 ( 数组形式 )
$envs: 环境变量 . 以数组 (key => value 形式 ) 方式传递给要执行程序的环境变量 .
int pcntl_for k (void):
创建一个子进程 , 该子进程与父进程仅仅是 PID( 进程号 ) 和 PPID( 父进程号 ) 不同 .
在父线程执行时返回创建的子进程 pid, 在子线程执行时返回 0, 创建子进程失败时会在父进程上下文返回 -1, 并引发 php 错误 .
理解这里的 fork 需要知道 : pcntl_fork 创建的是一个分支节点 , 相当于一个标记 , 父进程完成后 , 子进程会从标记处继续执行 , 也就是说 pcntl_fork 后面的代码分别被父进程和子进程执行了两遍 , 而两个进程在执行过程中得到的返回值是不同的 . 因此 , 才可以分离父子进程执行不同的代码 .
int pcntl_getpriority([int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]):
获取给定 $pid 对应的进程的优先级 , 默认是通过 getmypid() 获取到的值也就是当前进程 .
$pid: 如果没有指定 , 默认是当前进程 .
$process_identifier: PRIO_PGRP, PRIO_USER, PRIO_PROCESS 三者之一 , 默认 PRIO_PROCESS. 其中 PRIO_PGRP 指获取进程组的优先级 , PRIO_USER 指获取用户进程的优先级 , PRIO_PROCESS 指获取特定进程优先级 .
返回进程的优先级 , 或者在发生错误时返回 false, 值越小说明越优先
bool pcntl_setpriority(int $priority[, int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]:
设置进程的优先级 .
$priority: 优先级值 , -20 到 20 的范围内 , 默认优先级为 0. 值越小说明越优先 .
$pid: 如果没有指定 , 指当前进程
$process_identifier: 意义同 pcntl_getpriority 的 $process_identifier.
设置成功返回 TRUE, 失败返回 FALSE.
bool pcntl_signal_dispatch(void):
调用通过 pcntl_signal() 安装的即将发生的信号的处理器 .
调用成功返回 TRUE, 失败返回 false.
php 5.3.3 加入
bool pcntl_signal(int $signo, callback $handler[, bool $restart_syscalls = true]):
为指定的信号 $signo 安装一个新的信号处理器 $handler.
最后一个参数不明白意义 .
bool pcntl_sigprocmask(int $how, array $set[, array &$oldset]):
增加 , 删除或设置锁信号 , 具体的行为依赖于 $how 参数
$how: SIG_BLOCK 用于把信号增加到当前锁信号中 , SIG_UNBLOCK 用于把信号从当前锁信号中移除 , SIG_SETMASK 用于用给定的信号列表替换当前锁信号 .
$set: 要增加 , 移除或设置的信号列表 .
$oldset: 用于向调用者返回旧的锁定信号 .
成功返回 TRUE, 失败返回 FALSE.
int pcntl_sigtimedwait(array $set[, array &$siginfo[, int $seconds = 0[, int $nanoseconds = 0]]]):
pcntl_sigtimedwait 实际上和 pcntl_sigwaitinfo() 所做的是同样的事情 , 不过 pcntl_sigtimedwait 多了两个增强的参数 $seconds 和 $nanoseconds, 这样就允许脚本的停留时间有一个上限而不是无限制等待 .
$set: 需要等待的信号列表
$siginfo: 用来向调用者返回等待得到的信号的信息 , 信息内容见 pcntl_sigwaitinfo
$seconds: 超时的秒数
$nanoseconds: 超时的纳秒数
成功后 , pcntl_sigtimedwiat() 返回信号编号
int pcntl_sigwaitinfo(array $set[, array &$siginfo]):
挂起当前脚本的执行 , 直到接受到 $set 中的某个信号 , 如果其中的一个信号将要到达 ( 比如被 pcntl_sigprocmask 锁定 ) 那么 pcntl_sigwaitinfo 将会立刻返回
$set: 等待的信号列表
$siginfo: 用来向调用者返回等待得到的信号的信息 , 该信息包含以下内容 :
1. 所有信号都有以下三个信息 :
a) signo: 信号编号
b) errno: 错误号
c) code: 信号代码
2. SIGCHLD 信号特有的信息
a) status: 退出的值或信号
b) utime: 用户消耗时间
c) stime: 系统消耗时间
d) pid: 发送进程 id
e) uid: 发送进程的真实用户 id
3. SIGILL, SIGFPE, SIGSEGV, SIGBUS 拥有的信息
a) addr: 产生故障的内存位置
4. SIGPOLL 特有的信息 :
a) band: band event, 意义未知
b) fd: 文件描述符
函数成功运行返回信号编号
int pcntl_wait(int &$status[, int *options = 0]):
挂起当前进程直到一个子进程退出或直到一个信号要求终止当前进程或调用一个信号处理函数 . 如果子进程在调用时已经退出 ( 俗称成为了僵尸进程 ), 此函数会马上返回 , 所有的系统资源都将被释放 .
$status 用来保存子进程的状态信息 , 该状态信息由以下函数产生 : pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.
$options: 如果你的系统允许 wait3( 大多数的 BSD 类系统 ), 你可以提供一个可选的 options 参数 , 如果不提供这个参数 , wait 将会使用系统调用 , 如果系统不允许 wait3, 提供这个参数不会有任何影响 , $options 的值可以是 0 或者 WNOHANG 和 WUNTRACED 两个常数 .
函数返回退出的子进程的 PID, 或在错误时返回 -1, 或者如果提供 WNOHANG 作为 option(wait3 不可用的系统 ) 并且无有效子进程返回 0
僵尸进程 : 由于父进程在 fork 之后 , 无法预知子进程什么时候结束 , 所以子进程为了要留给父进程一些信息 , 会留下一个称作僵尸的数据结构 , 等待由父进程发起 wait 的操作来为它收尸 , 在子进程结束 ( 逻辑结束 ) 到父进程收尸前这一段时间子进程就被称为僵尸进程 , 在父进程结束后 , 所有的子进程会交由 Init 来负责 , 因此 , 如果父进程结束 , 僵尸进程都会被回收 , 但是 , 如果父进程永远不结束 , 这些僵尸进程就一直占用进程号 , 如果系统进程号耗尽 , 那么将导致无法启动新进程 , 因此 , 安全的做法是在父进程中为自己产生的子进程去收尸 .
int pcntl_waitpid(int $pid, int &$status[, int $options = 0]):
挂起当前进程直到给定 $pid 的子进程退出 , 或者当前进程接受到一个退出信号 , 或者接受到一 ige 信号去调用一个信号处理器 .
如果给定 $pid 对应的子进程在调用此函数时已经退出 ( 僵尸态 ), 函数立刻返回 , 所有的系统资源被释放 .
$pid: 进程号 , 小于 -1 表明等待的是进程组中的任何子进程 , 进程组号就是 $pid 的绝对值 . 等于 -1 表明等待任意紫禁城 , 与 pcntl_wait 函数行为一致 . 等于 0 代表等待与调用进程在同一组的子进程 , 大于 0 代表是特定的进程 .
$status: 用来由函数返回子进程状态 . 该状态信息由以下函数产生 : pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.
$options: 与 pcntl_wait 的 $options 意义相同
int pcntl_wexitstatus(int $status):
返回一个已经中断的子进程返回代码 , 此函数仅在 pcntl_wifexited 函数返回 TRUE 时有用 .
$status 参数是 pcntl_waitpid 产生的状态信息 .
bool pcntl_wifexited(int $status):
检查给定状态是否表明子进程是正常退出的 .
bool pcntl_wifsignaled(int $status):
检查给定状态是否表明子进程是由于收到某个信号退出的 .
bool pcntl_wifstopped(int $status):
检查 $status 是否能表明子进程当前已经停止 , 这个函数只有在作用于 pcntl_waitpid 函数使用的 WUNTRACED 作为 $options 参数的值时产生的 $status 上才有效 .
int pcntl_wstopsig(int $status):
通过分析 $status 返回使得子进程停止的信号的编号 , 这个函数只有在 pcntl_wifsignaled 返回 TRUE 时才有效 .
int pcntl_wtermsig(int $status):
返回使进程中断的信号编号 . 这个函数只有在 pcntl_wifsignaled 返回 TRUE 时才有效 .
做过PHP开发的程序员应该清楚,PHP中有很多内置的功能,掌握了它们,可以帮助你在做PHP开发时更加得心应手,本文将分享8个开发必备的PHP功能,个个都非常实用,希望各位PHP开发者能够掌握。
1、传递任意数量的函数参数
2、使用glob()查找文件
3、获取内存使用情况信息
4、获取CPU使用情况信息
5、获取系统常量
6、生成唯一的id
7、序列化
8、字符串压缩
1、传递任意数量的函数参数 我们在.NET或者JAVA编程中,一般函数参数个数都是固定的,但是PHP允许你使用任意个数的参数。下面这个示例向你展示了PHP函数的默认参数:
// 两个默认参数的函数
function foo($arg1 = ", $arg2 = ") {
echo "arg1: $arg1\n";
echo "arg2: $arg2\n";
}
foo('hello','world');
/* 输出:
arg1: hello
arg2: world
*/
foo();
/* 输出:
arg1:
arg2:
*/
下面这个示例是PHP的不定参数用法,其使用到了 func_get_args()方法:
// 是的,形参列表为空
function foo() {
// 取得所有的传入参数的数组
$args = func_get_args();
foreach ($args as $k => $v) {
echo "arg".($k+1).": $v\n";
}
}
foo();
/* 什么也不会输出 */
foo('hello');
/* 输出
arg1: hello
*/
foo('hello', 'world', 'again');
/* 输出
arg1: hello
arg2: world
arg3: again
*/
2、使用glob()查找文件 大部分PHP函数的函数名从字面上都可以理解其用途,但是当你看到 glob() 的时候,你也许并不知道这是用来做什么的,其实glob()和scandir() 一样,可以用来查找文件,请看下面的用法:
// 取得所有的后缀为PHP的文件
$files = glob('*.php');
print_r($files);
/* 输出:
Array
(
[0] => phptest.php
[1] => pi.php
[2] => post_output.php
[3] => test.php
)
*/
还可以查找多种后缀名:
// 取PHP文件和TXT文件
$files = glob('*.{php,txt}', GLOB_BRACE);
print_r($files);
/* 输出:
Array
(
[0] => phptest.php
[1] => pi.php
[2] => post_output.php
[3] => test.php
[4] => log.txt
[5] => test.txt
)
*/
还可以加上路径:
$files = glob('../images/a*.jpg');
print_r($files);
/* 输出:
Array
(
[0] => ../images/apple.jpg
[1] => ../images/art.jpg
)
*/
如果你想得到绝对路径,你可以调用 realpath() 函数:
$files = glob('../images/a*.jpg');
// applies the function to each array element
$files = array_map('realpath',$files);
print_r($files);
/* output looks like:
Array
(
[0] => C:\wamp\www\images\apple.jpg
[1] => C:\wamp\www\images\art.jpg
)
*/
3、获取内存使用情况信息 PHP的内存回收机制已经非常强大,你也可以使用PHP脚本获取当前内存的使用情况,调用memory_get_usage() 函数获取当期内存使用情况,调用memory_get_peak_usage() 函数获取内存使用的峰值。参考代码如下:
echo "Initial: ".memory_get_usage()." bytes \n";
/* 输出
Initial: 361400 bytes
*/
// 使用内存
for ($i = 0; $i < 100000; $i++) {
$array []= md5($i);
}
// 删除一半的内存
for ($i = 0; $i < 100000; $i++) {
unset($array[$i]);
}
echo "Final: ".memory_get_usage()." bytes \n";
/* prints
Final: 885912 bytes
*/
echo "Peak: ".memory_get_peak_usage()." bytes \n";
/* 输出峰值
Peak: 13687072 bytes
*/
4、获取CPU使用情况信息 获取了内存使用情况,也可以使用PHP的 getrusage()获取CPU使用情况,该方法在windows下不可用。
print_r(getrusage());
/* 输出
Array
(
[ru_oublock] => 0
[ru_inblock] => 0
[ru_msgsnd] => 2
[ru_msgrcv] => 3
[ru_maxrss] => 12692
[ru_ixrss] => 764
[ru_idrss] => 3864
[ru_minflt] => 94
[ru_majflt] => 0
[ru_nsignals] => 1
[ru_nvcsw] => 67
[ru_nivcsw] => 4
[ru_nswap] => 0
[ru_utime.tv_usec] => 0
[ru_utime.tv_sec] => 0
[ru_stime.tv_usec] => 6269
[ru_stime.tv_sec] => 0
)
*/
这个结构看上出很晦涩,除非你对CPU很了解。下面一些解释: ru_oublock: 块输出操作 ru_inblock: 块输入操作 ru_msgsnd: 发送的message ru_msgrcv: 收到的message ru_maxrss: 最大驻留集大小 ru_ixrss: 全部共享内存大小 ru_idrss:全部非共享内存大小 ru_minflt: 页回收 ru_majflt: 页失效 ru_nsignals: 收到的信号 ru_nvcsw: 主动上下文切换 ru_nivcsw: 被动上下文切换 ru_nswap: 交换区 ru_utime.tv_usec: 用户态时间 (microseconds) ru_utime.tv_sec: 用户态时间(seconds) ru_stime.tv_usec: 系统内核时间 (microseconds) ru_stime.tv_sec: 系统内核时间?(seconds) 要看到你的脚本消耗了多少CPU,我们需要看看"用户态的时间"和"系统内核时间"的值。秒和微秒部分是分别提供的,您可以把微秒值除以100万,并把它添加到秒的值后,可以得到有小数部分的秒数。
// sleep for 3 seconds (non-busy)
sleep(3);
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
/* 输出
User time: 0.011552
System time: 0
*/
是不占用系统时间的,我们可以来看下面的一个例子:
// loop 10 million times (busy)
for($i=0;$i<10000000;$i++) {
}
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
/* 输出
User time: 1.424592
System time: 0.004204
*/
这花了大约14秒的CPU时间,几乎所有的都是用户的时间,因为没有系统调用。传统时间是CPU花费在系统调用上的上执行内核指令的时间。下面是一个例子:
$start = microtime(true);
// keep calling microtime for about 3 seconds
while(microtime(true) – $start < 3) {
}
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
/* prints
User time: 1.088171
System time: 1.675315
*/
上面这个例子更耗CPU。
5、获取系统常量 PHP 提供非常有用的系统常量 可以让你得到当前的行号 (__LINE__),文件 (__FILE__),目录 (__DIR__),函数名 (__FUNCTION__),类名(__CLASS__),方法名(__METHOD__) 和名字空间 (__NAMESPACE__),很像C语言。
我们可以以为这些东西主要是用于调试,当也不一定,比如我们可以在include其它文件的时候使用?__FILE__ (当然,你也可以在 PHP 5.3以后使用 __DIR__ ),下面是一个例子。
// this is relative to the loaded script's path
// it may cause problems when running scripts from different directories
require_once('config/database.php');
// this is always relative to this file's path
// no matter where it was included from
require_once(dirname(__FILE__) . '/config/database.php');
下面是使用 __LINE__ 来输出一些debug的信息,这样有助于你调试程序:
// some code
// …
my_debug("some debug message", __LINE__);
/* 输出
Line 4: some debug message
*/
// some more code
// …
my_debug("another debug message", __LINE__);
/* 输出
Line 11: another debug message
*/
function my_debug($msg, $line) {
echo "Line $line: $msg\n";
}
6、生成唯一的id 很多朋友都利用md5()来生成唯一的编号,但是md5()有几个缺点:1、无序,导致数据库中排序性能下降。2、太长,需要更多的存储空间。其实PHP中自带一个函数来生成唯一的id,这个函数就是uniqid()。下面是用法:
// generate unique string
echo uniqid();
/* 输出
4bd67c947233e
*/
// generate another unique string
echo uniqid();
/* 输出
4bd67c9472340
*/
该算法是根据CPU时间戳来生成的,所以在相近的时间段内,id前几位是一样的,这也方便id的排序,如果你想更好的避免重复,可以在id前加上前缀,如:
// 前缀
echo uniqid('foo_');
/* 输出
foo_4bd67d6cd8b8f
*/
// 有更多的熵
echo uniqid(",true);
/* 输出
4bd67d6cd8b926.12135106
*/
// 都有
echo uniqid('bar_',true);
/* 输出
bar_4bd67da367b650.43684647
*/
7、序列化 PHP序列化功能大家可能用的比较多,也比较常见,当你需要把数据存到数据库或者文件中是,你可以利用PHP中的serialize() 和 unserialize()方法来实现序列化和反序列化,代码如下:
// 一个复杂的数组
$myvar = array(
'hello',
42,
array(1,'two'),
'apple'
);
// 序列化
$string = serialize($myvar);
echo $string;
/* 输出
a:4:{i:0;s:5:"hello";i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:"two";}i:3;s:5:"apple";}
*/
// 反序例化
$newvar = unserialize($string);
print_r($newvar);
/* 输出
Array
(
[0] => hello
[1] => 42
[2] => Array
(
[0] => 1
[1] => two
)
[3] => apple
)
*/
如何序列化成json格式呢,放心,php也已经为你做好了,使用php 5.2以上版本的用户可以使用json_encode() 和 json_decode() 函数来实现json格式的序列化,代码如下:
// a complex array
$myvar = array(
'hello',
42,
array(1,'two'),
'apple'
);
// convert to a string
$string = json_encode($myvar);
echo $string;
/* prints
["hello",42,[1,"two"],"apple"]
*/
// you can reproduce the original variable
$newvar = json_decode($string);
print_r($newvar);
/* prints
Array
(
[0] => hello
[1] => 42
[2] => Array
(
[0] => 1
[1] => two
)
[3] => apple
)
*/
8、字符串压缩 当我们说到压缩,我们可能会想到文件压缩,其实,字符串也是可以压缩的。PHP提供了 gzcompress() 和gzuncompress() 函数:
$string =
"Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Nunc ut elit id mi ultricies
adipiscing. Nulla facilisi. Praesent pulvinar,
sapien vel feugiat vestibulum, nulla dui pretium orci,
non ultricies elit lacus quis ante. Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Aliquam
pretium ullamcorper urna quis iaculis. Etiam ac massa
sed turpis tempor luctus. Curabitur sed nibh eu elit
mollis congue. Praesent ipsum diam, consectetur vitae
ornare a, aliquam a nunc. In id magna pellentesque
tellus posuere adipiscing. Sed non mi metus, at lacinia
augue. Sed magna nisi, ornare in mollis in, mollis
sed nunc. Etiam at justo in leo congue mollis.
Nullam in neque eget metus hendrerit scelerisque
eu non enim. Ut malesuada lacus eu nulla bibendum
id euismod urna sodales. ";
$compressed = gzcompress($string);
echo "Original size: ". strlen($string)."\n";
/* 输出原始大小
Original size: 800
*/
echo "Compressed size: ". strlen($compressed)."\n";
/* 输出压缩后的大小
Compressed size: 418
*/
// 解压缩
$original = gzuncompress($compressed);
几乎有50% 压缩比率。同时,你还可以使用 gzencode() 和 gzdecode() 函数来压缩,只不用其用了不同的压缩算法。
以上就是8个开发必备的PHP功能,是不是都很实用呢?
先看效果图
字体及字体文件的路径需要在类中$FontFilePath及$FontFileName中设置。如:
private static $FontFilePath = "static/font/"; //相对地本代码文件的位置
private static $FontFileName = array("3.ttf");// array("1.ttf", "2.ttf", "3.ttf", "4.ttf", "5.ttf", "6.ttf", "7.ttf", "8.ttf"); //
完整代码如下:
<?PHP
/**
说明: 验证码生成类,支持生成Gif图片验证码(带噪点,干扰线,网格,随机色背景,随机自定义字体,倾斜,Gif动画)
服务端:
$mod = strtolower(isset($_REQUEST["mod"]) ? $_REQUEST["mod"] : "");
if($mod == "code"){
echo SecurityCode::Draw(4, 1, 120, 30, 5, 10, 100, "secode");
die();
}
调用: <img src="/getcode/mod/code.html" onclick="this.src='/getcode/mod/code/amp;r/.html'+Math.round(Math.random(0)*1000)">
验证:
$reqCode = strtolower(isset($_REQUEST["secode"]) ? $_REQUEST["secode"] : ""); //请求的验证码
$sessionCode = strtolower(isset($_SESSION["secode"]) ? $_SESSION["secode"] : ""); //会话生成的验证码
if($reqCode != $sessionCode){
echo "安全验证码错误!";
die();
}
*/
$mod = strtolower(isset($_REQUEST["mod"]) ? $_REQUEST["mod"] : "");
if ($mod == "code") {
echo SecurityCode::Draw(4, 15, 100, 27, 10, 2, 100, "secode");
die();
}
//安全验证码类
class SecurityCode {
private static $Debug = 0;
private static $Code = '';
private static $Chars = 'bcdefhkmnrstuvwxyABCDEFGHKMNPRSTUVWXY34568';
//private static $Chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890';
private static $TextGap = 20;
private static $TextMargin = 5;
private static $FontFilePath = "static/font/"; //相对地本代码文件的位置
private static $FontFileName =array("3.ttf");// array("1.ttf", "2.ttf", "3.ttf", "4.ttf", "5.ttf", "6.ttf", "7.ttf", "8.ttf"); //
private static $Img = 'GIF89a'; //GIF header 6 bytes
private static $BUF = Array();
private static $LOP = 0;
private static $DIS = 2;
private static $COL = -1;
private static $IMG = -1;
/**
生成GIF图片验证
@param int $L 验证码长度
@param int $F 生成Gif图的帧数
@param int $W 宽度
@param int $H 高度
@param int $MixCnt 干扰线数
@param int $lineGap 网格线间隔
@param int $noisyCnt 澡点数
@param int $sessionName 验证码Session名称
*/
public static function Draw($L = 4, $F = 1, $W = 150, $H = 30, $MixCnt = 2, $lineGap = 0, $noisyCnt = 10, $sessionName = "Code") {
ob_start();
ob_clean();
for ($i = 0; $i < $L; $i++) {
self::$Code .= SubStr(self::$Chars, mt_rand(0, strlen(self::$Chars) - 1), 1);
}
if (!isset($_SESSION))
session_start();
$_SESSION[$sessionName] = strtolower(self::$Code);
$bgRGB = array(rand(0, 255), rand(0, 255), rand(0, 255));
//生成一个多帧的GIF动画
for ($i = 0; $i < $F; $i++) {
$img = ImageCreate($W, $H);
//背景色
$bgColor = imagecolorallocate($img, $bgRGB[0], $bgRGB[1], $bgRGB[2]);
ImageColorTransparent($img, $bgColor);
unset($bgColor);
//添加噪点
$maxNoisy = rand(0, $noisyCnt);
$noisyColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
for ($k = 0; $k <= $maxNoisy; $k++) {
imagesetpixel($img, rand(0, $W), rand(0, $H), $noisyColor);
}
//添加网格
if ($lineGap > 0) {
for ($m = 0; $m < ($W / $lineGap); $m++) { //竖线
imageline($img, $m * $lineGap, 0, $m * $lineGap, $H, $noisyColor);
}
for ($n = 0; $n < ($H / $lineGap); $n++) { //横线
imageline($img, 0, $n * $lineGap, $W, $n * $lineGap, $noisyColor);
}
}
unset($noisyColor);
// 添加干扰线
for ($k = 0; $k < $MixCnt; $k++) {
$wr = mt_rand(0, $W);
$hr = mt_rand(0, $W);
$lineColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
imagearc($img, $W - floor($wr / 2), floor($hr / 2), $wr, $hr, rand(90, 180), rand(180, 270), $lineColor);
unset($lineColor);
unset($wr, $hr);
}
//第一帧忽略文字
if ($i != 0 || $F <= 1) {
//文字
$foreColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
for ($j = 0; $j < $L; $j++) {
$fontFile = self::$FontFilePath . self::$FontFileName[rand(0, count(self::$FontFileName) - 1)];
if (!file_exists($fontFile))
imagestring($img, 4, self::$TextMargin + $j * self::$TextGap, ($H - rand($H / 2, $H)), self::$Code[$j], $foreColor);
else
imageTTFtext($img, rand(15, 18), rand(-15, 15), self::$TextMargin + $j * self::$TextGap, ($H - rand(7, 10)), $foreColor, $fontFile, self::$Code[$j]);
}
unset($foreColor);
}
ImageGif($img);
Imagedestroy($img);
$Imdata[] = ob_get_contents();
OB_clean();
}
unset($W, $H, $B);
if (self::$Debug) {
echo $_SESSION['code'];
echo '<pre>', Var_Dump($Imdata), '</pre>';
die();
}
header('Content-type:image/gif');
return self::CreateGif($Imdata, 20);
unset($Imdata);
}
private static function CreateGif($GIF_src, $GIF_dly = 10, $GIF_lop = 0, $GIF_dis = 0, $GIF_red = 0, $GIF_grn = 0, $GIF_blu = 0, $GIF_mod = 'bin') {
if (!is_array($GIF_src) && !is_array($GIF_tim)) {
throw New Exception('Error:' . __LINE__ . ',Does not supported function for only one image!!');
die();
}
self::$LOP = ($GIF_lop > -1) ? $GIF_lop : 0;
self::$DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2;
self::$COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ? ($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1;
for ($i = 0, $src_count = count($GIF_src); $i < $src_count; $i++) {
if (strToLower($GIF_mod) == 'url') {
self::$BUF[] = fread(fopen($GIF_src[$i], 'rb'), filesize($GIF_src[$i]));
} elseif (strToLower($GIF_mod) == 'bin') {
self::$BUF[] = $GIF_src[$i];
} else {
throw New Exception('Error:' . __LINE__ . ',Unintelligible flag (' . $GIF_mod . ')!');
die();
}
if (!(Substr(self::$BUF[$i], 0, 6) == 'GIF87a' Or Substr(self::$BUF[$i], 0, 6) == 'GIF89a')) {
throw New Exception('Error:' . __LINE__ . ',Source ' . $i . ' is not a GIF image!');
die();
}
for ($j = (13 + 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07))), $k = TRUE; $k; $j++) {
switch (self::$BUF[$i]{$j}) {
case '!':
if ((substr(self::$BUF[$i], ($j + 3), 8)) == 'NETSCAPE') {
throw New Exception('Error:' . __LINE__ . ',Could not make animation from animated GIF source (' . ($i + 1) . ')!');
die();
}
break;
case ';':
$k = FALSE;
break;
}
}
}
self::AddHeader();
for ($i = 0, $count_buf = count(self::$BUF); $i < $count_buf; $i++) {
self::AddFrames($i, $GIF_dly);
}
self::$Img .= ';';
return (self::$Img);
}
private static function AddHeader() {
$i = 0;
if (ord(self::$BUF[0]{10}) & 0x80) {
$i = 3 * (2 << (ord(self::$BUF[0]{10}) & 0x07));
self::$Img .= substr(self::$BUF[0], 6, 7);
self::$Img .= substr(self::$BUF[0], 13, $i);
self::$Img .= "!\377\13NETSCAPE2.0\3\1" . chr(self::$LOP & 0xFF) . chr((self::$LOP >> 8) & 0xFF) . "\0";
}
unset($i);
}
private static function AddFrames($i, $d) {
$L_str = 13 + 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07));
$L_end = strlen(self::$BUF[$i]) - $L_str - 1;
$L_tmp = substr(self::$BUF[$i], $L_str, $L_end);
$G_len = 2 << (ord(self::$BUF[0]{10}) & 0x07);
$L_len = 2 << (ord(self::$BUF[$i]{10}) & 0x07);
$G_rgb = substr(self::$BUF[0], 13, 3 * (2 << (ord(self::$BUF[0]{10}) & 0x07)));
$L_rgb = substr(self::$BUF[$i], 13, 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07)));
$L_ext = "!\xF9\x04" . chr((self::$DIS << 2) + 0) . chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0";
if (self::$COL > -1 && ord(self::$BUF[$i]{10}) & 0x80) {
for ($j = 0; $j < (2 << (ord(self::$BUF[$i]{10}) & 0x07)); $j++) {
if (ord($L_rgb{3 * $j + 0}) == (self::$COL >> 0) & 0xFF && ord($L_rgb{3 * $j + 1}) == (self::$COL >> 8) & 0xFF && ord($L_rgb{3 * $j + 2}) == (self::$COL >> 16) & 0xFF) {
$L_ext = "!\xF9\x04" . chr((self::$DIS << 2) + 1) . chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0";
break;
}
}
}
switch ($L_tmp{0}) {
case '!':
$L_img = substr($L_tmp, 8, 10);
$L_tmp = substr($L_tmp, 18, strlen($L_tmp) - 18);
break;
case ',':
$L_img = substr($L_tmp, 0, 10);
$L_tmp = substr($L_tmp, 10, strlen($L_tmp) - 10);
break;
}
if (ord(self::$BUF[$i]{10}) & 0x80 && self::$IMG > -1) {
if ($G_len == $L_len) {
if (self::Compare($G_rgb, $L_rgb, $G_len)) {
self::$Img .= ($L_ext . $L_img . $L_tmp);
} else {
$byte = ord($L_img{9});
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord(self::$BUF[0]{10}) & 0x07);
$L_img{9} = chr($byte);
self::$Img .= ($L_ext . $L_img . $L_rgb . $L_tmp);
}
} else {
$byte = ord($L_img{9});
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord(self::$BUF[$i]{10}) & 0x07);
$L_img{9} = chr($byte);
self::$Img .= ($L_ext . $L_img . $L_rgb . $L_tmp);
}
} else {
self::$Img .= ($L_ext . $L_img . $L_tmp);
}
self::$IMG = 1;
}
private static function Compare($G_Block, $L_Block, $Len) {
for ($i = 0; $i < $Len; $i++) {
if ($G_Block{3 * $i + 0} != $L_Block{3 * $i + 0} || $G_Block{3 * $i + 1} != $L_Block{3 * $i + 1} || $G_Block{3 * $i + 2} != $L_Block{3 * $i + 2}) {
return (0);
}
}
return (1);
}
}
用法在类开头的注释里。