1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: namespace Jyxo\Rpc;
15:
16: /**
17: * Abstract class for sending RPC requests.
18: *
19: * @category Jyxo
20: * @package Jyxo\Rpc
21: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
22: * @license https://github.com/jyxo/php/blob/master/license.txt
23: * @author Jaroslav HanslĂk
24: */
25: abstract class Client
26: {
27: /**
28: * Server address.
29: *
30: * @var string
31: */
32: protected $url = '';
33:
34: /**
35: * Time limit for communication with RPC server (seconds).
36: *
37: * @var integer
38: */
39: protected $timeout = 5;
40:
41: /**
42: * Parameters for creating RPC requests.
43: *
44: * @var array
45: */
46: protected $options = array();
47:
48: /**
49: * Timer name.
50: *
51: * @var string
52: */
53: private $timer = '';
54:
55: /**
56: * Whether to use request profiler.
57: *
58: * @var boolean
59: */
60: private $profiler = false;
61:
62: /**
63: * Creates client instance and eventually sets server address.
64: *
65: * @param string $url Server address
66: */
67: public function __construct($url = '')
68: {
69: if (!empty($url)) {
70: $this->setUrl($url);
71: }
72: }
73:
74: /**
75: * Sets server address.
76: *
77: * @param string $url Server address
78: * @return \Jyxo\Rpc\Client
79: */
80: public function setUrl($url)
81: {
82: $this->url = (string) $url;
83:
84: return $this;
85: }
86:
87: /**
88: * Sets timeout.
89: *
90: * @param integer $timeout Call timeout
91: * @return \Jyxo\Rpc\Client
92: */
93: public function setTimeout($timeout)
94: {
95: $this->timeout = (int) $timeout;
96:
97: return $this;
98: }
99:
100: /**
101: * Changes client settings.
102: *
103: * @param string $key Parameter name
104: * @param mixed $value Parameter value
105: * @return \Jyxo\Rpc\Client
106: */
107: public function setOption($key, $value)
108: {
109: if (isset($this->options[$key])) {
110: $this->options[$key] = $value;
111: }
112: return $this;
113: }
114:
115: /**
116: * Returns certain parameter or whole array of parameters if no parameter name is provided.
117: *
118: * @param string $key Parameter name
119: * @return mixed
120: */
121: public function getOption($key = '')
122: {
123: if (isset($this->options[$key])) {
124: return $this->options[$key];
125: }
126: return $this->options;
127: }
128:
129: /**
130: * Turns request profiler on.
131: *
132: * @return \Jyxo\Rpc\Client
133: */
134: public function enableProfiler()
135: {
136: $this->profiler = true;
137: return $this;
138: }
139:
140: /**
141: * Turns request profiler off.
142: *
143: * @return \Jyxo\Rpc\Client
144: */
145: public function disableProfiler()
146: {
147: $this->profiler = false;
148: return $this;
149: }
150:
151: /**
152: * Sends a request and fetches a response from the server.
153: *
154: * @param string $method Method name
155: * @param array $params Method parameters
156: * @return mixed
157: * @throws \BadMethodCallException If no server address was provided
158: * @throws \Jyxo\Rpc\Exception On error
159: */
160: abstract public function send($method, array $params);
161:
162: /**
163: * Processes request data and fetches response.
164: *
165: * @param string $contentType Request content-type
166: * @param string $data Request data
167: * @return string
168: * @throws \BadMethodCallException If no server address was provided
169: * @throws \Jyxo\Rpc\Exception On error
170: */
171: protected function process($contentType, $data)
172: {
173: // Server address must be defined
174: if (empty($this->url)) {
175: throw new \BadMethodCallException('No server address was provided.');
176: }
177:
178: // Headers
179: $headers = array(
180: 'Content-Type: ' . $contentType,
181: 'Content-Length: ' . strlen($data)
182: );
183:
184: // Open a HTTP channel
185: $channel = curl_init();
186: curl_setopt($channel, CURLOPT_URL, $this->url);
187: curl_setopt($channel, CURLOPT_RETURNTRANSFER, true);
188: curl_setopt($channel, CURLOPT_CONNECTTIMEOUT, 0);
189: curl_setopt($channel, CURLOPT_TIMEOUT, $this->timeout);
190: curl_setopt($channel, CURLOPT_HTTPHEADER, $headers);
191: curl_setopt($channel, CURLOPT_POSTFIELDS, $data);
192:
193: // Send a request
194: $response = curl_exec($channel);
195:
196: // Error sending the request
197: if (0 !== curl_errno($channel)) {
198: $error = curl_error($channel);
199: curl_close($channel);
200:
201: throw new Exception($error);
202: }
203:
204: // Wrong code
205: $code = curl_getinfo($channel, CURLINFO_HTTP_CODE);
206: if ($code >= 300) {
207: $error = sprintf('Response error from %s, code %d.', $this->url, $code);
208: curl_close($channel);
209:
210: throw new Exception($error);
211: }
212:
213: // Close the channel
214: curl_close($channel);
215:
216: // Return the response
217: return $response;
218: }
219:
220: /**
221: * Starts profiling.
222: *
223: * @return \Jyxo\Rpc\Client
224: */
225: protected function profileStart()
226: {
227: // Set start time
228: if ($this->profiler) {
229: $this->timer = \Jyxo\Timer::start();
230: }
231:
232: return $this;
233: }
234:
235: /**
236: * Finishes profiling.
237: *
238: * @param string $type Request type
239: * @param string $method Method name
240: * @param array $params Method parameters
241: * @param mixed $response Server response
242: * @return \Jyxo\Rpc\Client
243: */
244: protected function profileEnd($type, $method, array $params, $response)
245: {
246: // Profiling has to be turned on
247: if ($this->profiler) {
248: static $totalTime = 0;
249: static $requests = array();
250:
251: // Get elapsed time
252: $time = \Jyxo\Timer::stop($this->timer);
253:
254: $totalTime += $time;
255: $requests[] = array(strtoupper($type), (string) $method, $params, $response, sprintf('%0.3f', $time * 1000));
256:
257: // Send to FirePHP
258: \Jyxo\FirePhp::table(
259: sprintf('Jyxo RPC Profiler (%d requests took %0.3f ms)', count($requests), sprintf('%0.3f', $totalTime * 1000)),
260: array('Type', 'Method', 'Request', 'Response', 'Time'),
261: $requests,
262: 'Rpc'
263: );
264: }
265:
266: return $this;
267: }
268: }
269: