Cake/Network/CakeSocket.php

1 <?php
2 /**
3 * Cake Socket connection class.
4 *
5 * PHP 5
6 *
7 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
8 * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
9 *
10 * Licensed under The MIT License
11 * Redistributions of files must retain the above copyright notice.
12 *
13 * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
14 * @link http://cakephp.org CakePHP(tm) Project
15 * @package Cake.Network
16 * @since CakePHP(tm) v 1.2.0
17 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
18 */
19  
20 App::uses('Validation', 'Utility');
21  
22 /**
23 * Cake network socket connection class.
24 *
25 * Core base class for network communication.
26 *
27 * @package Cake.Network
28 */
29 class CakeSocket {
30  
31 /**
32 * Object description
33 *
34 * @var string
35 */
36 public $description = 'Remote DataSource Network Socket Interface';
37  
38 /**
39 * Base configuration settings for the socket connection
40 *
41 * @var array
42 */
43 protected $_baseConfig = array(
44 'persistent' => false,
45 'host' => 'localhost',
46 'protocol' => 'tcp',
47 'port' => 80,
48 'timeout' => 30
49 );
50  
51 /**
52 * Configuration settings for the socket connection
53 *
54 * @var array
55 */
56 public $config = array();
57  
58 /**
59 * Reference to socket connection resource
60 *
61 * @var resource
62 */
63 public $connection = null;
64  
65 /**
66 * This boolean contains the current state of the CakeSocket class
67 *
68 * @var boolean
69 */
70 public $connected = false;
71  
72 /**
73 * This variable contains an array with the last error number (num) and string (str)
74 *
75 * @var array
76 */
77 public $lastError = array();
78  
79 /**
80 * Constructor.
81 *
82 * @param array $config Socket configuration, which will be merged with the base configuration
83 * @see CakeSocket::$_baseConfig
84 */
85 public function __construct($config = array()) {
86 $this->config = array_merge($this->_baseConfig, $config);
87 if (!is_numeric($this->config['protocol'])) {
88 $this->config['protocol'] = getprotobyname($this->config['protocol']);
89 }
90 }
91  
92 /**
93 * Connect the socket to the given host and port.
94 *
95 * @return boolean Success
96 * @throws SocketException
97 */
98 public function connect() {
99 if ($this->connection != null) {
100 $this->disconnect();
101 }
102  
103 $scheme = null;
104 if (isset($this->config['request']) && $this->config['request']['uri']['scheme'] == 'https') {
105 $scheme = 'ssl://';
106 }
107  
108 if ($this->config['persistent'] == true) {
109 $this->connection = @pfsockopen($scheme . $this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
110 } else {
111 $this->connection = @fsockopen($scheme . $this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
112 }
113  
114 if (!empty($errNum) || !empty($errStr)) {
115 $this->setLastError($errNum, $errStr);
116 throw new SocketException($errStr, $errNum);
117 }
118  
119 $this->connected = is_resource($this->connection);
120 if ($this->connected) {
121 stream_set_timeout($this->connection, $this->config['timeout']);
122 }
123 return $this->connected;
124 }
125  
126 /**
127 * Get the host name of the current connection.
128 *
129 * @return string Host name
130 */
131 public function host() {
132 if (Validation::ip($this->config['host'])) {
133 return gethostbyaddr($this->config['host']);
134 }
135 return gethostbyaddr($this->address());
136 }
137  
138 /**
139 * Get the IP address of the current connection.
140 *
141 * @return string IP address
142 */
143 public function address() {
144 if (Validation::ip($this->config['host'])) {
145 return $this->config['host'];
146 }
147 return gethostbyname($this->config['host']);
148 }
149  
150 /**
151 * Get all IP addresses associated with the current connection.
152 *
153 * @return array IP addresses
154 */
155 public function addresses() {
156 if (Validation::ip($this->config['host'])) {
157 return array($this->config['host']);
158 }
159 return gethostbynamel($this->config['host']);
160 }
161  
162 /**
163 * Get the last error as a string.
164 *
165 * @return string Last error
166 */
167 public function lastError() {
168 if (!empty($this->lastError)) {
169 return $this->lastError['num'] . ': ' . $this->lastError['str'];
170 }
171 return null;
172 }
173  
174 /**
175 * Set the last error.
176 *
177 * @param integer $errNum Error code
178 * @param string $errStr Error string
179 * @return void
180 */
181 public function setLastError($errNum, $errStr) {
182 $this->lastError = array('num' => $errNum, 'str' => $errStr);
183 }
184  
185 /**
186 * Write data to the socket.
187 *
188 * @param string $data The data to write to the socket
189 * @return boolean Success
190 */
191 public function write($data) {
192 if (!$this->connected) {
193 if (!$this->connect()) {
194 return false;
195 }
196 }
197 $totalBytes = strlen($data);
198 for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) {
199 $rv = fwrite($this->connection, substr($data, $written));
200 if ($rv === false || $rv === 0) {
201 return $written;
202 }
203 }
204 return $written;
205 }
206  
207 /**
208 * Read data from the socket. Returns false if no data is available or no connection could be
209 * established.
210 *
211 * @param integer $length Optional buffer length to read; defaults to 1024
212 * @return mixed Socket data
213 */
214 public function read($length = 1024) {
215 if (!$this->connected) {
216 if (!$this->connect()) {
217 return false;
218 }
219 }
220  
221 if (!feof($this->connection)) {
222 $buffer = fread($this->connection, $length);
223 $info = stream_get_meta_data($this->connection);
224 if ($info['timed_out']) {
225 $this->setLastError(E_WARNING, __d('cake_dev', 'Connection timed out'));
226 return false;
227 }
228 return $buffer;
229 }
230 return false;
231 }
232  
233 /**
234 * Disconnect the socket from the current connection.
235 *
236 * @return boolean Success
237 */
238 public function disconnect() {
239 if (!is_resource($this->connection)) {
240 $this->connected = false;
241 return true;
242 }
243 $this->connected = !fclose($this->connection);
244  
245 if (!$this->connected) {
246 $this->connection = null;
247 }
248 return !$this->connected;
249 }
250  
251 /**
252 * Destructor, used to disconnect from current connection.
253 *
254 */
255 public function __destruct() {
256 $this->disconnect();
257 }
258  
259 /**
260 * Resets the state of this Socket instance to it's initial state (before Object::__construct got executed)
261 *
262 * @param array $state Array with key and values to reset
263 * @return boolean True on success
264 */
265 public function reset($state = null) {
266 if (empty($state)) {
267 static $initalState = array();
268 if (empty($initalState)) {
269 $initalState = get_class_vars(__CLASS__);
270 }
271 $state = $initalState;
272 }
273  
274 foreach ($state as $property => $value) {
275 $this->{$property} = $value;
276 }
277 return true;
278 }
279  
280 }
281  
282