分类目录归档:php

不依赖于web服务器的php测试环境

<?php
$scriptInvokedFromCli =
    isset($_SERVER['argv'][0]) && $_SERVER['argv'][0] === 'server.php';

if($scriptInvokedFromCli) {
    echo 'starting server on port 3000' . PHP_EOL;
    exec('php -S localhost:3000 -t public server.php');
} else {
    return routeRequest();
}

function routeRequest()
{
    $comments = file_get_contents('_comments.json');
    switch($_SERVER["REQUEST_URI"]) {
        case '/':
            echo file_get_contents('./public/index.html');
            break;
        case '/comments.json':
            if($_SERVER['REQUEST_METHOD'] === 'POST') {
                $commentsDecoded = json_decode($comments, true);
                $commentsDecoded[] = ['author'  => $_POST['author'],
                                      'text'    => $_POST['text']];

                $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT);
                file_put_contents('_comments.json', $comments);
            }
            header('Content-Type: application/json');
            echo $comments;
            break;
        default:
            return false;
    }
}

一个贪睡的sleeper

php写的命令行服务,有时候在不需要执行的时候,需要尽可能的降低消耗资源,最简单的降低消耗就是sleep了。
单次循环sleep间隔时间,有时候不满足需求,当前的sleeper采用了类似tcp的重传定时器,
不过,这个东西越sleep越贪睡奥,需要适度控制。

<?php

abstract class sleeper {

	protected $init_sleep_time;
	protected $sleep_time = 0;
	
	public function sleeper($init_sleep_time){
		$this->sleep_time = $init_sleep_time;	
		$this->init_sleep_time = $init_sleep_time;
	}

	public function real_sleep(){
		if($this->need_sleep()){
			echo $this->sleep_time . "\n";
			sleep($this->sleep_time);
			$this->sleep_time += 3;
		} else {
			$this->sleep_time = $this->init_sleep_time;
		}
	}
	
	public abstract function need_sleep();

}

class test_sleeper extends sleeper {
	
	public function sleeper($init_sleep_time = 1){
		$this->sleep_time = $init_sleep_time;	
	}

	public function need_sleep(){
		return true;	
	}

}

$sl = new test_sleeper(1);
for($i = 0; $i< 10; $i++){
	$sl->real_sleep();
}

php父子进程通信

多进程demo里介绍过,如何fork子进程。
但是,谁家的孩子谁管,你fork了得记得处理啊,不然哪天捣点乱啥的,是吧。

进程与进程之间通信的方式有很多种,这里用最简单的信号通信。当我们kill -9 一个进程的时候,实际上就是向该进程发送一个终止信号,之前已经绑定了信号处理器,所以,我们可以用其他进程通知另一个进程做出相应的反应(kill -9 的时候是退出)。父子进程通信也不例外,父进程绑定信号处理器,用来协调子进程,子进程向父进程发送信号,让父进程有相应的反应。

绑定信号处理器:pcntl_signal http://cn2.php.net/manual/en/function.pcntl-signal.php#70161
发送信号:posix_kill http://cn2.php.net/manual/en/function.posix-kill.php
需要注意一点,信号的不可重入性,当一个子进程的信号处理器正处理时,将忽略其他的信号处理。所以,需要pcntl_watipid
http://cn2.php.net/manual/en/function.pcntl-waitpid.php 来等待。

运用上边的理论,实现了一个固定子进程处理任务的程序:
父进程fork子进程,然后绑定SIGCHLD信号,子进程结束触发父进程的SIGCHLD信号处理器,末尾主循环,统计子进程的个数,当低于指定值时,fork新的子进程:代码如下:

<?php                                                                                                                         

declare(ticks=1);
$child_nums = 0;
function catch_signal($signal){
    global $child_nums;
    while( pcntl_waitpid(-1, $status, WNOHANG) > 0 ) { 
        if($signal == SIGCHLD){
            $child_nums--;  
        }   
    }   
}

function fork_do($c){
    global $child_nums;
    while($c--) {
        if(pcntl_fork() == 0 ) { 
            real_do();              
        } else {
            $child_nums++;  
        }   
    }   
}

function real_do(){
    sleep(rand(1,3));
    exit();
}

$manage_pid = getmypid();
$fork_nums = 4;
pcntl_signal(SIGCHLD,'catch_signal');

fork_do($fork_nums);

while($child_nums != 0){ 
    echo "Childs " . $child_nums . "\n";    
    if($child_nums < $fork_nums) {
        fork_do($fork_nums - $child_nums);  
    }   
}         

注:要看动态效果显得很酷的话,把sleep改成usleep或者干脆去掉吧。