PHP进程及进程间通信

一、引言

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。换句话说就是,在系统调度多个cpu的时候,一个程序的基本单元。进程对于大多数的语言都不是一个陌生的概念,作为”世界上最好的语言PHP”当然也例外。

二、环境

php中的进程是以扩展的形式来完成。通过这些扩展,我们能够很轻松的完成进程的一系列动作。

  • pcntl扩展:主要的进程扩展,完成进程创建于等待操作。
  • posix扩展:完成posix兼容机通用api,如获取进程id,杀死进程等。
  • sysvmsg扩展:实现system v方式的进程间通信之消息队列。
  • sysvsem扩展:实现system v方式的信号量。
  • sysvshm扩展:实现system v方式的共享内存。
  • sockets扩展:实现socket通信。

这些扩展只能在linux/mac中使用,window下是不支持。最后建议php版本为5.5+。

相关代码:进程相关代码

三、简单的例子

一个简单的PHP多进程例子,该例子中,一个子进程,一个父进程。子进程输出5次,退出程序。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();
$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        echo "({$pid})child progress is running! {$i} \n";
        $rand = rand(1,3);
        sleep($rand);
    }
    exit("({$pid})child progress end!\n");
} else {
    // 父进程执行程序
    $childList[$pid] = 1;
}
// 等待子进程结束
pcntl_wait($status);
echo "({$parentPid})main progress end!";

完美,终于创建了一个子进程,一个父进程。完了么?没有,各个进程之间相互独立的,没有任何交集,使用范围严重受到现在。怎么办,哪就进程间通信(interprogress communication)呗。

四、进程间通信(IPC)

通常linux中的进程通信方式有:消息队列、信号量、共享内存、信号、管道、socket。

1.消息队列

消息队列是存放在内存中的一个队列。如下代码将创建3个生产者子进程,2个消费者子进程。这5个进程将通过消息队列通信。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";$childList = array();
// 创建消息队列,以及定义消息类型(类似于数据库中的库)
$id = ftok(__FILE__,'m');
$msgQueue = msg_get_queue($id);
const MSG_TYPE = 1;
// 生产者
function producer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $str = "({$pid})progress create! {$i}";
        msg_send($msgQueue,MSG_TYPE,$str);
        $rand = rand(1,3);
        sleep($rand);
    }
}
// 消费者
function consumer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 6;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $rel = msg_receive($msgQueue,MSG_TYPE,$msgType,1024,$message);
        echo "{$message} | consumer({$pid}) destroy \n";
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress('producer');
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 2个写进程
for ($i = 0; $i < 2; $i ++ ) {
    $pid = createProgress('consumer');
    $childList[$pid] = 1;
    echo "create consumer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
echo "({$parentPid})main progress end!\n";

由于消息队列去数据是,只有一个进程能去到,所以不需要额外的锁或信号量。

2. 信号量与共享内存

信号量:是系统提供的一种原子操作,一个信号量,同时只有你个进程能操作。一个进程获得了某个信号量,就必须被该进程释放掉。

共享内存:是系统在内存中开辟的一块公共的内存区域,任何一个进程都可以访问,在同一时刻,可以有多个进程访问该区域,为了保证数据的一致性,需要对该内存区域加锁或信号量。

以下,创建多个进程修改内存中的同一个值。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();

// 创建共享内存,创建信号量,定义共享key
$shm_id = ftok(__FILE__,'m');
$sem_id = ftok(__FILE__,'s');
$shareMemory = shm_attach($shm_id);
$signal = sem_get($sem_id);
const SHARE_KEY = 1;
// 生产者
function producer(){
    global $shareMemory;
    global $signal;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        // 获得信号量
        sem_acquire($signal);
        
        if (shm_has_var($shareMemory,SHARE_KEY)){
            // 有值,加一
            $count = shm_get_var($shareMemory,SHARE_KEY);
            $count ++;
            shm_put_var($shareMemory,SHARE_KEY,$count);
            echo "({$pid}) count: {$count}\n";
        }else{
            // 无值,初始化
            shm_put_var($shareMemory,SHARE_KEY,0);
            echo "({$pid}) count: 0\n";
        }
        // 用完释放
        sem_release($signal);
        
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress('producer');
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
// 释放共享内存与信号量
shm_remove($shareMemory);
sem_remove($signal);
echo "({$parentPid})main progress end!\n";

3.信号

信号是一种系统调用。通常我们用的kill命令就是发送某个信号给某个进程的。具体有哪些信号可以在liunx/mac中运行kill -l查看。下面这个例子中,父进程等待5秒钟,向子进程发送sigint信号。子进程捕获信号,掉信号处理函数处理。


$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";

// 定义一个信号处理函数
function sighandler($signo) {
    $pid = posix_getpid();
    echo "{$pid} progress,oh no ,I'm killed!\n";
    exit(1);
}

$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    // 注册信号处理函数
    declare(ticks=10);
    pcntl_signal(SIGINT, "sighandler");
    $pid = posix_getpid();
    while(true){
        echo "{$pid} child progress is running!\n";
        sleep(1);
    }
    exit("({$pid})child progress end!\n");
}else{
    // 父进程执行程序
    $childList[$pid] = 1;
    // 5秒后,父进程向子进程发送sigint信号.
    sleep(5);
    posix_kill($pid,SIGINT);
    sleep(5);
}
echo "({$parentPid})main progress end!\n";

4.管道(有名管道)

管道是比较常用的多进程通信手段,管道分为无名管道与有名管道,无名管道只能用于具有亲缘关系的进程间通信,而有名管道可以用于同一主机上任意进程。这里只介绍有名管道。下面的例子,子进程写入数据,父进程读取数据。

// 定义管道路径,与创建管道
$pipe_path = '/data/test.pipe';
if(!file_exists($pipe_path)){
    if(!posix_mkfifo($pipe_path,0664)){
        exit("create pipe error!");
    }
}
$pid = pcntl_fork();
if($pid == 0){
    // 子进程,向管道写数据
    $file = fopen($pipe_path,'w');
    while (true){
        fwrite($file,'hello world');
        $rand = rand(1,3);
        sleep($rand);
    }
    exit('child end!');
}else{
    // 父进程,从管道读数据
    $file = fopen($pipe_path,'r');
    while (true){
        $rel = fread($file,20);
        echo "{$rel}\n";
        $rand = rand(1,2);
        sleep($rand);
    }
}

5.socket

socket即我们常说的套接字编程。这个待补充。原文地址:https://www.jianshu.com/p/08bcf724196b

PHP基于swoole多进程操作示例

https://www.jb51.net/article/167448.htm

这篇文章主要介绍了PHP基于swoole多进程操作,结合实例形式分析了php使用swoole多进程实现多个任务同时执行以及大任务划分成多个小任务相关操作技巧,需要的朋友可以参考下

java

本文实例讲述了PHP基于swoole多进程操作。分享给大家供大家参考,具体如下:

多个任务同时执行

将顺序执行的任务,转化为并行执行(任务在逻辑上可以并行执行)
比如,我们要对已知的用户数据进行判断,是否需要发送邮件和短信,如果需要发送则发送。

不使用多进程时,我们首先判断是否发送邮件,如果需要则发送;然后再判断是否需要发送短信,如果需要则发送。如果发送邮件耗时2s,发送短信耗时2s,那么我们完成任务大概需要4s左右的时间。

如果我们使用多线程的话,可以开两个线程,一个用于处理邮件,一个用于处理短信,则耗时一共需要2s左右,处理时间缩短了一半。

123456789101112131415161718192021222324252627282930313233343536373839<?php/*** Created by PhpStorm.* User: zhezhao* Date: 2016/10/20* Time: 10:37*/$info = array("sendmail"=>1,"mailto"=>"12345@qq.com","sendsms"=>1,"smsto"=>"123456");echo "start:".date("Y-m-d H:i:s").PHP_EOL;$mail_process = new swoole_process('sendMail',true);$mail_process->start();$sms_process = new swoole_process('sendSMS',true);$sms_process->start();//主进程输出子进程范围内容echo $mail_process->read();echo PHP_EOL;echo $sms_process->read();echo PHP_EOL;echo "end:".date("Y-m-d H:i:s").PHP_EOL;//并行函数function sendMail(swoole_process $worker){global $info;if($info['sendmail']==1){sleep(2);$worker->write("send mail to ".$info['mailto']);}}function sendSMS(swoole_process $worker){global $info;if($info['sendmail']==1){sleep(2);$worker->write("send sms to ".$info['smsto']);}}
这里写图片描述

大任务划分成多个小任务

将循环执行的任务,划分为多个进程执行,提高工作效率

假设我们现在有一个通过curl抓取网页内容的需求,需要抓取10个网页,url地址通过数组读取,每个curl耗时2s。如果我们通过for循环来抓取这10个网页,需要耗时20s,使用多进程我们可以将任务划分成5份,分别由5个进程执行,每个进程抓取2个url,并发执行,共耗时4s,效率提高5倍。

123456789101112131415161718192021222324252627282930313233343536<?php/*** Created by PhpStorm.* User: zhezhao* Date: 2016/10/20* Time: 10:51*/$url_arr = array();for ($i=0;$i<10;$i++){$url_arr[] = "www.baidu.com?wd=".$i;}echo "start:".date("Y-m-d H:i:s").PHP_EOL;$workers = array();for ($i=0;$i<5;$i++){$process = new swoole_process('getContents',true);$process->start();$process->write($i);$workers[] = $process;}//主进程数据结果foreach ($workers as $process){echo $process->read();echo PHP_EOL;}echo "end:".date("Y-m-d H:i:s").PHP_EOL;function getContents(swoole_process $worker){$i = $worker->read();global $url_arr;$res1 = execCurl($url_arr[($i*2)]);$res2 = execCurl($url_arr[($i*2+1)]);echo $res1.PHP_EOL.$res2;}function execCurl($url){sleep(2);return "handle ".$url." finished";}
这里写图片描述

总结

以上两种情况,本质上都是将逻辑上没有先后关系的任务,用多个进程程并发执行,提高效率。

php机制本身不提供多线程的操作,ptcl扩展提供了php操作linux多进程的接口。

个人感觉swoole的多进程process方法更加方便一些。

关于两者的比较:http://wiki.swoole.com/wiki/page/214.html

参考文章:
https://segmentfault.com/a/1190000002946586

更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP进程与线程操作技巧总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《PHP数组(Array)操作技巧大全》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总

PHP面试题及答案

面试筛选问题

  1. 给你四个坐标点,如何判断它们能不能组成一个矩形?如判断([0,0],[0,1],[1,1],[1,0])能组成一个矩形

答:

//获取两个点之间的长度的平方

    //不计算边长是为了后面方便进行比较

    function getLength($point1, $point2){

        $res = pow($point1[0]-$point2[0], 2) + pow($point1[1]-$point2[1], 2);

        return $res;

    }

    function isRectangle($point1, $point2, $point3, $point4){

        //任意两点相同 不可能组成矩形

        if($point1 == $point2 || $point1 == $point3 || $point1 == $point4 || $point2 == $point3 || $point2 == $point4 || $point3 == $point4){

            return false;

        }

        //将所有边长平方放在一个数组内

        $arr = [];

        $arr[] = getLength($point1, $point2);

        $arr[] = getLength($point1, $point3);

        $arr[] = getLength($point1, $point4);

        $arr[] = getLength($point2, $point3);

        $arr[] = getLength($point2, $point4);

        $arr[] = getLength($point3, $point4);

        //去重

        $arr = array_unique($arr);

        $arr_count = count($arr);

        //正方形也是矩形

        if($arr_count == 3 || $arr_count == 2){

            $max_length = max($arr);

            $min_length = min($arr);

            $other_length = array_diff($arr, [$max_length, $min_length]);

            //勾股定理

            if($min_length + $other_length = $max_length){

                return true;

            } else {

                return false;

            }

        } else {

            return false;

        }

}

调用

var_dump(isRectangle([0,0],[0,1],[1,1],[1,0]));

2、 下面代码结果是?

$a=array(‘1′,’2′,’3’);

$b=&$a;

$a=array(‘a’,’b’,’c’);

print_r($a);

print_r($b);

答:

Array

(

    [0] => a

    [1] => b

    [2] => c

)

Array

(

    [0] => a

    [1] => b

    [2] => c

)

3、mySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

答:(1).保留热点数据

(2).保证redis只存入20w的数据。

首先热点数据就是频繁访问的数据,我们可以通过redis的淘汰策略来完成,这里推荐Allkeys-lru淘汰策略,该淘汰策略从数据集中挑选最近最少使用的数据删除。

再者需要解决的问题是只存入20w的数据,目前来看只能通过redis的内存限制来实现,计算20w数据使用内存大小进行预设置内存大小实现。或许内存大小不好计算,但是我们可以只记录热点数据的主键id,redis只保存热点数据主键id,而主键id一般都是定长的,大小利于计算。

4、禁用cookie后怎么使用session。请结合你做过的项目对上面的每一点进行举例剖析

答:Session URL重写,保证在客户端禁用或不支持COOKIE时,仍然可以使用Session。

如:

<?php session_start();

$_SESSION[‘url’] = ‘http://www.ailiaw.top’;

echo ‘个页面取到的session值:’.$_SESSION[‘url’];

?>

<a href=”session_a.php?<?php print session_name() ?>=<?php print session_id() ?>”>另一个页面</a>

关于PHP中浏览器禁止Cookie后,Session能使用吗?

sessionid是存储在cookie中的,解决方案如下:

Session URL重写,保证在客户端禁用或不支持COOKIE时,仍然可以使用Session

session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当 程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。 保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于 SEEESIONID。

关于PHP中浏览器禁止Cookie后,Session能使用吗?我们来做些测试,然后说明原理。我建立两个文件session_test.php和session_a.php内容分别是:





  1. <?php
  2. session_start();
  3. $_SESSION['url'] = 'http://www.phpddt.com';
  4. echo '这个页面取到的session值:'.$_SESSION['url'];
  5. echo "<a href='session_a.php'>另一个页面</a>";




  1. <?php
  2. session_start();
  3. echo “看这里是否获取到session的值:”.$_SESSION[‘url’];

运行session_test.php结果:

点击另一个页面,默认当然是可以获取的,但是当我禁止cookie后呢?
看看session_a.php结果:

可以看到禁用之后是不能使用cookie的。

解决方法一:URL重写PHP中的session在默认情况下是使用客户端的 Cookie 来保存session id的,所以当客户端的cookie出现问题的时候就会影响session了。但是Session并不完全依赖Cookie,它还可以通过URL Get自动传递session id的。这需要你 设置php.ini中的 session.use_trans_sid = 1 或者 编译时打开了–enable-trans-sid选项 ,让PHP 自动跨页传递session id 。当 session.use_trans_sid 为有效时, session.use_only_cookies一定要设置为无效0

好,这样,把上面session_test.php和session_a.php改造下就可以使用sesson了:

//session_test.php

<?phpsession_start();$_SESSION['url'] = 'http://www.phpddt.com';echo '这个页面取到的session值:'.$_SESSION['url'];?><a href="session_a.php">另一个页面</a>

//session_a.php

<?php//session_a.phpsession_start();echo "看这里是否获取到session的值:".$_SESSION['url']; ?><a href="session_b.php">另一个页面b</a>

//解决方法二:手动传递url





  1. <?php
  2. session_start();
  3. $_SESSION['url'] = 'http://www.phpddt.com';
  4. echo '这个页面取到的session值:'.$_SESSION['url'];
  5. ?>
  6. <a href="session_a.php?<?php print session_name() ?>=<?php print session_id() ?>">另一个页面</a>




  1. <?php
  2. session_id($_GET[‘PHPSESSID’]);
  3. session_start();
  4. echo “看这里是否获取到session的值:”.$_SESSION[‘url’];

//解决方法三:隐藏表单的方法基本原理同上。

途径3举例说明:login.html按 Ctrl+C 复制代码<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Login</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>
</head>
<body>
请登录:
<form name=”login” method=”post” action=”mylogin1.php”>
用户名:<input type=”text” name=”name”><br>
口 令:<input type=”password” name=”pass”><br>
<input type=”submit” value=”登录”>
</form>
</body>
</html>按 Ctrl+C 复制代码

mylogin1.php按 Ctrl+C 复制代码<?php
$name=$_POST[’name’];
$pass=$_POST[’pass’];
if(!$name || !$pass) {
echo “用户名或密码为空,请<a href=”login.html”>重新登录</a>”;
die();
}
if (!($name==”youngong” && $pass==”123″) {
echo “用户名或密码不正确,请<a href=”login.html”>重新登录</a>”;
die();
}
//注册用户
ob_start();
session_start();
$_SESSION[’user’]= $name;
$psid=session_id();
$fp=fopen(“e:\tmp\phpsid.txt”,”w+”;
fwrite($fp,$psid);
fclose($fp);
//身份验证成功,进行相关操作
echo “已登录<br>”;
echo “<a href=”mylogin2.php”>下一页</a>”;
?>按 Ctrl+C 复制代码

mylogin2.php按 Ctrl+C 复制代码<?php
$fp=fopen(“e:\tmp\phpsid.txt”,”r”;
$sid=fread($fp,1024);
fclose($fp);
session_id($sid);
session_start();
if(isset($_SESSION[’user’]) && $_SESSION[’user’]=”laogong” {
echo “已登录!”;
}
else {
//成功登录进行相关操作
echo “未登录,无权访问”;
echo “请<a href=”login.html”>登录</a>后浏览”;
die();
}
?>按 Ctrl+C 复制代码

请关闭cookie再测试,用户名:youngong 密码:123 这是通过文件保存session id的,文件是:e:\tmp\phpsid.txt。至于用数据库的方法,就不举例子了,与文件的操作方法类似。以上方法有一个共同点,就是在前一页取得session id,想办法传递到下一页,在下一页的session_start();之前加代码session_id(传过来的session id);用关系数据库或内存数据库redis以及文本存储session时需要建立用户id和其sessionid的一个映射关闭,应对多用户时调用不同的session文件。希望能为您提供一些参考。

给四个坐标点,判断能否构成一个矩形(php)

矩形:

  矩形对角线相等,且四个角为直角。所以可以根据勾股定理判定。

思路:

  首先判断坐标点是否有重复,然后四个坐标点可以求得它们两两之间的距离,只要两条短边的平方相加等于长边平方即可判定它为矩形。

注意:

  正方形是特殊的矩形。

代码附上:

复制代码
<?php 

    //获取两个点之间的长度的平方
    //不计算边长是为了后面方便进行比较
    function getLength($point1, $point2){
        $res = pow($point1[0]-$point2[0], 2) + pow($point1[1]-$point2[1], 2);
        return $res;
    }

    function judgeRectangle($point1, $point2, $point3, $point4){
        //任意两点相同 不可能组成矩形
        if($point1 == $point2 || $point1 == $point3 || $point1 == $point4 || $point2 == $point3 || $point2 == $point4 || $point3 == $point4){
            return false;
        }

        //将所有边长平方放在一个数组内
        $arr = [];
        $arr[] = getLength($point1, $point2);
        $arr[] = getLength($point1, $point3);
        $arr[] = getLength($point1, $point4);
        $arr[] = getLength($point2, $point3);
        $arr[] = getLength($point2, $point4);
        $arr[] = getLength($point3, $point4);

        //去重
        $arr = array_unique($arr);
        $arr_count = count($arr);

        //正方形也是矩形
        if($arr_count == 3 || $arr_count == 2){
            $max_length = max($arr);
            $min_length = min($arr);
            $other_length = array_diff($arr, [$max_length, $min_length]);

            //勾股定理
            if($min_length + $other_length = $max_length){
                return true;
            } else {
                return false;
            }

        } else {
            return false;
        }
    }

    var_dump(judgeRectangle([0,0], [0,5], [2,0], [2,6]));
复制代码

PHP 实现简单的树形列表。

首先是数据库设计

我设计的一个列数为三列的表Treenodes,这三列分别用来存储当前节点的id、节点名称、父节点

SQL如下

CREATE TABLE `treenodes` (
  `id` int(11) NOT NULL,
  `node_name` varchar(50) DEFAULT NULL,
  `pid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

前台页面已经搭好。只要能构建形如下面的html代码,前台的js就能很好的展现树形列表了。

<ul>
   <li>根节点
       <ul>
           <li>子节点1</li>
           <li>子节点2
               <ul>
                   <li>孙子节点1</li>
                   <li>孙子节点1</li>
               </ul>
           </li>
       </ul>
   </li>
</ul>

先放置一些测试数据供展现。

INSERT INTO `treenodes` VALUES ('1', 'INMIX 培训知识库', '-1');
INSERT INTO `treenodes` VALUES ('2', 'IT部门', '1');
INSERT INTO `treenodes` VALUES ('3', 'HR部门', '1');
INSERT INTO `treenodes` VALUES ('4', '线上培训', '1');
INSERT INTO `treenodes` VALUES ('5', '线下培训', '1');
INSERT INTO `treenodes` VALUES ('6', 'ERP系统方面 ', '2');
INSERT INTO `treenodes` VALUES ('7', '软件应用方面', '2');
INSERT INTO `treenodes` VALUES ('8', 'IT设备应用方面', '2');
INSERT INTO `treenodes` VALUES ('9', '店铺系统方面', '2');
INSERT INTO `treenodes` VALUES ('10', '钉钉轻松小秘书日程管理(V1.0).pdf', '6');

第一步先把数据从数据库中取出来

$conn = new mysqli('xxx.xxx.xxx.xxx', 'userxxx', 'xxxxxx', 'QuotationSystem');
/* check connnection */
if ($conn ->connect_errno){
      printf("Connect failed: %s \n", $conn->connect_error);
      exit();
}
if ($q = $conn ->query('select * from treenodes a')){
  $result = $q-> fetch_all();
}

第二步是将数据存放在一个多维的数组里边

定义的数据结构是:
用数组来存放一个节点, 如果这个节点是末端节点,则该数组,只有一个字符串元素,
如果这个节点存在子节点,则在当前数组中,增加一个数组,所有的子节点的元素存放在这个数组中。

 // 定义 book tree
            function build_book_tree($data, $parentid){
                $nodeName = '';
                $child = [];
                foreach($data as $key => $val){
                    //如果当前遍历的项跟查询的id相同,则获取节点的名称
                    if ($val[0] == $parentid){
                        $nodeName = $val[1];
                    }
                    //如果当前节点的父节点跟查询的节点相同,则通过递归,获取他的子节点,并将结果,赋给当前节点节点数组中
                    if ($val[2] == $parentid){
                        array_push($child, build_book_tree($data, $val[0]));
                    }
                }
                //不存放空节点
                if (count($child) > 0)
                    return array($nodeName, $child);
                else 
                    return $nodeName;
            }

第三步 是生成html 列表元素, 这是我今天卡壳的地方。递归用的不太好,后来整理了一下思路,问题就迎刃而解了。

思路:先尝试把一个节点,拥有一个子节点的数据结构构建成html树。然后再构建复杂的情情况。函数只要实现<li>节点名<ul><li>子节点一</li>…</ul></li>就行了。递归函数只帮我们构建最简单的形式,赋予它更多的意义,实现起来困难程度就会加倍。 所有构建html树的实现应该是这样的:

function build_html_list($data)
            {
                if (is_array($data) and count($data) == 2 and is_string($data[0])){
                    // 输出节点名
                    echo "<li>".$data[0];
                    // 输出子节点
                    if (is_array($data[1])){
                        echo "<ul>";
                        foreach ($data[1] as $item){
                            if (is_string($item)){
                                echo "<li>".$item."</li>";
                            } 
                            else{
                                build_html_list($item);    
                            }
                        }
                        echo "</ul>";
                    }
                    echo "</li></ul>";
                }
                echo "</li>";
            }

输出它的时候,需要给上述方法添加“<ul>”html元素。

php实现简单洗牌算法

<?php
 /**
* 简单洗牌算法
  */

 $card_num=54; //牌数
 print_r(wash_card($card_num));

 function wash_card($card_num)
 {
     $cards=$tmp=array();
     for($i=0;$i<$card_num;$i++){
         $tmp[$i]=$i;
     }

     for($i=0;$i<$card_num;$i++){
         $index=rand(0,$card_num-$i-1);
         $cards[$i]=$tmp[$index];
         unset($tmp[$index]);
         $tmp=array_values($tmp);
     }
     return $cards;
 }

 ?>

PHP冒泡排序

这个应该是最大众的排序了。也算是简单排序算法中知名度最高的。

冒泡排序的思路是怎么样的呢?

首先就是先比较相邻的两个元素,如果满足条件,就交换,否则不动。

然后是再比较接下来的两个相邻的元素,然后满足条件就交换,否则依然不动。

依次循环操作下去,最终一个元素,会固定在最下边。

举例:

数组有5个元素从小到大排序,首先第一个和第二个比,第一个比第二个大,就交换,否则不操作

然后第二个和第三个比较,第二个大于第三个则交换,否则依然不动。直到对比到第四个和第五个对比。

经过一轮的循环对比,最大的数字就下沉到最下边了。小的数字逐渐向上浮出。

复制代码
 1 // 冒泡排序
 2 function bubble_sort($arr)
 3 {
 4     $len = count($arr);
 5     for ($i = 0; $i < $len -1; $i++) {//循环对比的轮数
 6         for ($j = 0; $j < $len - $i - 1; $j++) {//当前轮相邻元素循环对比
 7             if ($arr[$j] > $arr[$j + 1]) {//如果前边的大于后边的
 8                 $tmp = $arr[$j];//交换数据
 9                 $arr[$j] = $arr[$j + 1];
10                 $arr[$j + 1] = $tmp;
11             }
12         }
13     }
14     return $arr;
15 }
16 $arr = [5,2,4,7,9,4,2,6,8,3];
17 print_r(bubble_sort($arr));
复制代码

以上代码呢,是标准的冒泡排序,代码实现也完全符合冒泡排序的说法。

但是,在实际上,有很多人把冒泡排序写成了另一种样子。

复制代码
 1 //排序
 2 function paixu($arr)
 3 {
 4     $len = count($arr);
 5     for ($i = 0; $i < $len - 1; $i++) {//循环比对的轮数
 6         for ($j = $i + 1; $j < $len; $j++) {//从第二个开始循环,循环到最后一个,逐一和第一个比较
 7             if ($arr[$i] > $arr[$j]) {//前边大于后边的则交换
 8                 $tmp = $arr[$i];
 9                 $arr[$i] = $arr[$j];
10                 $arr[$j] = $tmp;
11             }
12         }
13     }
14     return $arr;
15 }
16 $arr = [5,2,4,7,9,4,2,6,8,3];
17 print_r(paixu($arr));
复制代码

这个写法,大致看上去和标准的冒泡排序写法非常相似,思路也非常像。

这个排序的思路是怎么样呢?

首先,假设还是实现数组的从小到大的排序。

第一轮循环,用数组的第一个元素,逐一和第二个一直到最后一个元素比对,每次对比,只要谁小,谁就和第一个元素交换。

第二轮循环,则从第二个元素开始,逐一和第三个到最后一个比对,谁小,就和第二个元素交换。一直这样循环比对,直到最后。

这每轮循环,都把最小的那个元素上浮到了最顶上。

标准的冒泡排序是每一轮把最大的确定在最后,而这个排序没一轮把最小的确定在最前边。

可以说这两个真的算是比较像了,当然,这个排序的形式,也很像选择排序

目前为止,咱这也说不好,这个后边的排序,到底算冒泡排序?还算选择排序呢?或者他有自己的名字。

有了解的朋友,还望多指教。

比特币api大全

在开发比特币应用时,除了使用自己搭建的节点,也可以利用第三方提供的比特币api,来获取市场行情、进行交易支付、查询账户余额等。这些第三方api不一定遵循标准的比特币rpc接口规范,但往往会利用自身的数据存储来增加比特币行情api、交易到账通知api、比特币rest api等,因此可以作为比特币应用开发的有益补充。本文介绍比特币开发人员常用的第三方比特币api的特点及访问地址。

如果要快速掌握比特币的对接与应用开发,推荐汇智网的在线互动课程:

Java比特币开发详解 —–
Php比特币开发详解 —–
C#比特币开发详解

1、blockchain.com比特币api

blockchain.com的比特币api是最受欢迎的比特币开发第三方api之一,提供支付处理、钱包服务、市场行情数据等功能。blockchain.com的比特币api同时还提供了针对多种语言的封装开发包,例如python、java、.net(c#)、ruby、php和node。

地址:https://www.blockchain.com/api

2、chain.so比特币api

chain.so的特色是除了提供比特币api,还额外提供的一些山寨币的api,例如莱特币、达世币等。

chian.so的比特币api,提供了获取地址、区块、市场行情等方面的功能,也支持交易广播。免费用户有5次请求/秒的限流。

地址:https://chain.so/api

3、block.io比特币api

block.io的比特币api包括基本的钱包服务、实时通知与即时支付转发等功能,支持web hook和websocket。对于免费用户,有3次/秒的限流。

地址:https://www.block.io/docs

4、chainquery.com比特币api

chainquery.com提供了比特币rpc api的web访问接口,你可以在网页里直接输入并执行标准的比特币rpc命令。

地址:http://chainquery.com/bitcoin-api

5、coinbase.com比特币api

作为老牌的交易所,coinbase.com也提供比特币api,功能包括生成比特币地址、买/卖比特币、钱包服务、实时行情接收、支付到账通知等。

接入coinbase.com的比特币api需要使用OAuth2,这是令人不开心的一点。

地址:https://developers.coinbase.com/

6、blockcypher.com比特币api

blockcyper.com提供rest风格的比特币api,功能涵盖地址、钱包、交易等常见需求,同时提供事件和hook机制,以便应用实时得到通知。

地址: https://www.blockcypher.com/dev/bitcoin/

7、bitcoinchain.com比特币api

bitcoinchain.com提供rest和stream两种方式的比特币api,功能包括基本的比特币区块链数据交互和市场行情通知。免费用户有1次请求/秒的限流。

地址:https://bitcoinchain.com/api

8、coindesk.com比特币api

coindesk.com专注于提供比特币价格指数方面的api,包括实时BPI数据和历史BPI数据。coindesk.com的比特币api不支持与比特币区块链的交互。

地址:https://www.coindesk.com/api

9、blockchain.info比特币api

作为专业的比特币区块链浏览服务提供商,blockchain.info专注于提供比特币区块数据查询api,如果你希望查询某个地址相关的历史交易信息,bitchain.info的比特币api是最佳选择。

地址:https://blockchain.info/q

10、btc.com比特币api

btc.com的比特币api主要提供比特币区块链交易数据的查询功能,但是不支持比特币交易的广播。

地址: https://btc.com/api-doc

51行代码实现简单的PHP区块链

今年区块链特别火,我也很火啊。我火什么呢。前几年,公众平台出现,还得花时间去学去看,后来小程序出现,又得花时间精力去学去看。现在比特币、以太坊等去中心化货币带起了区块链的发展。还得学。

没办法,技术改变师姐。不,是改变世界。

前些天看到python写的50行代码实现的简单区块链。今天让我们PHP也实现一下区块链的简单流程。

phper或其他人如有需要可加PHP区块链交流群(370648191/201923866)。

只有一个类、4个方法。可直接运行。

<?php/** * 简单的PHP区块链 * @author Yoper * @PHP技术交流QQ群 370648191 * @Email chen.yong.peng@foxmail.com * @wechat YoperMan */namespace common\library\block;/** * 区块结构 */class block{    private $index;    private $timestamp;    private $data;    private $previous_hash;    private $random_str;    private $hash;    public function __construct($index,$timestamp,$data,$random_str,$previous_hash)    {        $this->index=$index;        $this->timestamp=$timestamp;        $this->data=$data;        $this->previous_hash=$previous_hash;        $this->random_str=$random_str;        $this->hash=$this->hash_block();    }    public function __get($name){        return $this->$name;    }    private function hash_block(){        $str=$this->index.$this->timestamp.$this->data.$this->random_str.$this->previous_hash;        return hash("sha256",$str);    }}/** * 创世区块 * @return \common\library\block\block */function create_genesis_block(){    return new \common\library\block\block(0, time(),"第一个区块",0,0);}/** * 挖矿,生成下一个区块 * 这应该是一个复杂的算法,但为了简单,我们这里挖到前1位是数字就挖矿成功。 * @param \common\library\block\block $last_block_obj */function dig(\common\library\block\block $last_block_obj){    $random_str = $last_block_obj->hash.get_random();    $index=$last_block_obj->index+1;    $timestamp=time();    $data='I am block '.$index;    $block_obj = new \common\library\block\block($index,$timestamp,$data,$random_str,$last_block_obj->hash);        //前一位不是数字    if(!is_numeric($block_obj->hash{0})){        return false;    }    //数数字,返回块    return $block_obj;}/** * 验证区块 * 这也是一个复杂的过程,为了简单,我们这里直接返回正确 * @param array $data */function verify(\common\library\block\block $last_block_obj){    return true;}/** * 生成随机字符串 * @param int $len * @return string */function get_random($len=32){    $str="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";    $key = "";    for($i=0;$i<$len;$i++)    {        $key.= $str{mt_rand(0,32)};//随机数    }    return $key;}  header("Content-type:text/html;charset=utf-8");//生成第一个区块$blockchain=[\common\library\block\create_genesis_block()];//模拟生成其他区块,我们直接循环生成。实际中,还需要跟踪互联网上多台机器上链的变化,像比特币会有工作量证明等算法,达到条件了才生成区块等//我们的链是一个数组,实际生产中应该保存下来$previous_block = $blockchain[0];for($i=0;$i<=10;$i++){    if(!($new_block=dig($previous_block))){        continue;    }    $blockchain[]=$new_block;    $previous_block=$new_block;        //告诉大家新增了一个区块    echo "区块已加入链中.新区块是 : {$new_block->index}<br/>";    echo "新区块哈希值是 : {$new_block->hash}<br/>";    print_r($new_block);    echo "<br/><br/>";}

以上文件可以直接运行。运行结果如下:

完善之后,就可以发行自己的货币或者智能合约了。

https://github.com/liexusong/blockchain-php/tree/v2.0