1<?php
2/*
3  +----------------------------------------------------------------------+
4  | Yar - Light, concurrent RPC framework                                |
5  +----------------------------------------------------------------------+
6  | Copyright (c) 2012-2013 The PHP Group                                |
7  +----------------------------------------------------------------------+
8  | This source file is subject to version 3.01 of the PHP license,      |
9  | that is bundled with this package in the file LICENSE, and is        |
10  | available through the world-wide-web at the following url:           |
11  | http://www.php.net/license/3_01.txt                                  |
12  | If you did not receive a copy of the PHP license and are unable to   |
13  | obtain it through the world-wide-web, please send a note to          |
14  | license@php.net so we can mail you a copy immediately.               |
15  +----------------------------------------------------------------------+
16  | Author:  Syber       <ibar@163.com>                                  |
17  |          Xinchen Hui <laruence@php.net>                              |
18  +----------------------------------------------------------------------+
19*/
20
21/***************************************************************************
22 *   用法:按照正常yar调用方法,只是把类名修改为本调试类名。
23 *   1
24 *   $yar = new Yar_Debug_Client('http://yar_server/path');
25 *   var_dump($yar->call('method', $params));			//这里通常能看到server端代码是否有错误,错误在那里。
26 *   2
27 *   Yar_Debug_Concurrent_Client::call('http://yar_server/path', 'method', $params, $callback, $errcallback);
28 *   Yar_Debug_Concurrent_Client::loop();		//在callback也可以看到server反馈
29 *
30 *
31 *   也可以用这个封装类做一个在线调试 exp:
32 *   http://hk.yafdev.com/yar_server_response_viewer.php
33 *
34 *
35***************************************************************************/
36
37class Yar_Debug_Client {
38	private $url;
39	public function __construct($url) {
40		$this->url = $url;
41	}
42
43	public function call($method, $arguments) {
44		return Yar_Debug_Transports::exec($this->url, Yar_Debug_Protocol::Package($method, $arguments));
45	}
46
47	public function __call($method, $arguments) {
48		return Yar_Debug_Transports::exec($this->url, Yar_Debug_Protocol::Package($method, $arguments));
49	}
50}
51
52class Yar_Debug_Concurrent_Client {
53	private static $data = array();
54	public static function call($uri, $m, $params = null, $callback = null, $errorcallback = null) {
55		$package = Yar_Debug_Protocol::Package($m, $params);
56		self::$data[] = array(
57			'uri'		=>	$uri,
58			'data'		=>	$package,
59			'callback'	=>	$callback,
60			'errcb'		=>	$errorcallback,
61		);
62		return $package['transaction'];
63	}
64
65	public static function loop() {
66		foreach(self::$data as $v) {
67			$ret = Yar_Debug_Transports::exec($v['uri'], $v['data']);
68			if (strpos('HTTP/1.1 200 OK', $ret['header']) !== false) {
69				$call = $v['callback'];
70				$return = true;
71			} else {
72				$call = $v['errcb'];
73				$return = false;
74			}
75			if (is_callable($call)) {
76				$o = $ret['o'];
77				$r = $ret['r'];
78				call_user_func($call, $r, $o);
79			}
80		}
81		return $return;
82	}
83}
84
85class Yar_Debug_Protocol {
86	public static function Package($m, $params) {
87		$struct = array(
88			'i'	=>	time(),
89			'm'	=>	$m,
90			'p'	=>	$params,
91		);
92		$body = str_pad('PHP', 8, chr(0)) . serialize($struct);
93
94		$transaction = sprintf('%08x', mt_rand());
95
96		$header = '';
97		$header = $transaction;						//transaction id
98		$header .= sprintf('%04x', 0);				//protocl version
99		$header .= '80DFEC60';						//magic_num, default is: 0x80DFEC60
100		$header .= sprintf('%08x', 0);				//reserved
101		$header .= sprintf('%064x', 0);				//reqeust from who
102		$header .= sprintf('%064x', 0);				//request token, used for authentication
103		$header .= sprintf('%08x', strlen($body));	//request body len
104
105		$data = '';
106		for($i = 0; $i < strlen($header); $i = $i + 2)
107			$data .= chr(hexdec('0x' . $header[$i] . $header[$i + 1]));
108		$data .= $body;
109		return array(
110			'transaction'	=>	$transaction,
111			'data'			=>	$data
112		);
113	}
114}
115
116class Yar_Debug_Transports {
117	public static function exec($url, $data) {
118		$urlinfo = parse_url($url);
119        $port = isset($urlinfo["port"])? $urlinfo["port"] : 80;
120		$path = $urlinfo['path'] . (!empty($urlinfo['query']) ? '?' . $urlinfo['query'] : '') . (!empty($urlinfo['fragment']) ? '#' . $urlinfo['fragment'] : '');
121
122		$in = "POST {$path} HTTP/1.1\r\n";
123		$in .= "Host: {$urlinfo['host']}\r\n";
124		$in .= "Content-Type: application/octet-stream\r\n";
125		$in .= "Connection: Close\r\n";
126		$in .= "Hostname: {$urlinfo['host']}\r\n";
127		$in .= "Content-Length: " . strlen($data['data']) . "\r\n\r\n";
128
129		$address = gethostbyname($urlinfo['host']);
130
131		$fp = fsockopen($address, $port, $err, $errstr);
132		if (!$fp) {
133			die ("cannot conncect to {$address} at port {$port} '{$errstr}'");
134		}
135		fwrite($fp, $in . $data['data'], strlen($in . $data['data']));
136
137		$f_out = '';
138		while ($out = fread($fp, 2048))
139			$f_out .= $out;
140
141		$tmp = explode("\r\n\r\n", $f_out);
142		return array (
143			'header'	=>	$tmp[0],
144            'body'      =>  $tmp[1],
145			'return'	=>	unserialize(substr($tmp[1], 82 + 8)),
146		);
147		fclose($fp);
148	}
149}
150?>
151