123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Socket;
  36. use Hoa\Consistency;
  37. /**
  38. * Class \Hoa\Socket.
  39. *
  40. * Socket analyzer.
  41. *
  42. * @copyright Copyright © 2007-2017 Hoa community
  43. * @license New BSD License
  44. */
  45. class Socket
  46. {
  47. /**
  48. * Address type: IPv6.
  49. *
  50. * @const int
  51. */
  52. const ADDRESS_IPV6 = 0;
  53. /**
  54. * Address type: IPv4.
  55. *
  56. * @const int
  57. */
  58. const ADDRESS_IPV4 = 1;
  59. /**
  60. * Address type: domain.
  61. *
  62. * @const int
  63. */
  64. const ADDRESS_DOMAIN = 2;
  65. /**
  66. * Address type: path.
  67. *
  68. * @const int
  69. */
  70. const ADDRESS_PATH = 3;
  71. /**
  72. * Address.
  73. *
  74. * @var string
  75. */
  76. protected $_address = null;
  77. /**
  78. * Address type. Please, see the self::ADDRESS_* constants.
  79. *
  80. * @var int
  81. */
  82. protected $_addressType = 0;
  83. /**
  84. * Port.
  85. *
  86. * @var int
  87. */
  88. protected $_port = -1;
  89. /**
  90. * Transport.
  91. *
  92. * @var string
  93. */
  94. protected $_transport = null;
  95. /**
  96. * Whether the socket is secured or not.
  97. *
  98. * @var bool
  99. */
  100. protected $_secured = false;
  101. /**
  102. * Constructor.
  103. *
  104. * @param string $uri URI.
  105. */
  106. public function __construct($uri)
  107. {
  108. $this->setURI($uri);
  109. return;
  110. }
  111. /**
  112. * Set URI.
  113. *
  114. * @param string $uri URI.
  115. * @return string
  116. * @throws \Hoa\Socket\Exception
  117. */
  118. public function setURI($uri)
  119. {
  120. $m = preg_match(
  121. '#(?<scheme>[^:]+)://' .
  122. '(?:\[(?<ipv6_>[^\]]+)\]:(?<ipv6_port>\d+)$|' .
  123. '(?<ipv4>(\*|\d+(?:\.\d+){3}))(?::(?<ipv4_port>\d+))?$|' .
  124. '(?<domain>[^:]+)(?::(?<domain_port>\d+))?$|' .
  125. '(?<ipv6>.+)$)#',
  126. $uri,
  127. $matches);
  128. if (0 === $m) {
  129. throw new Exception(
  130. 'URI %s is not recognized (it is not an IPv6, IPv4 nor ' .
  131. 'domain name).',
  132. 0,
  133. $uri
  134. );
  135. }
  136. $this->setTransport($matches['scheme']);
  137. if (isset($matches['ipv6_']) && !empty($matches['ipv6_'])) {
  138. $this->_address = $matches['ipv6_'];
  139. $this->_addressType = self::ADDRESS_IPV6;
  140. $this->setPort((int) $matches['ipv6_port']);
  141. } elseif (isset($matches['ipv6']) && !empty($matches['ipv6'])) {
  142. $this->_address = $matches['ipv6'];
  143. $this->_addressType = self::ADDRESS_IPV6;
  144. } elseif (isset($matches['ipv4']) && !empty($matches['ipv4'])) {
  145. $this->_address = $matches['ipv4'];
  146. $this->_addressType = self::ADDRESS_IPV4;
  147. if ('*' === $this->_address) {
  148. $this->_address = '0.0.0.0';
  149. }
  150. if (isset($matches['ipv4_port'])) {
  151. $this->setPort((int) $matches['ipv4_port']);
  152. }
  153. } elseif (isset($matches['domain'])) {
  154. $this->_address = $matches['domain'];
  155. if (false !== strpos($this->_address, '/')) {
  156. $this->_addressType = self::ADDRESS_PATH;
  157. } else {
  158. $this->_addressType = self::ADDRESS_DOMAIN;
  159. }
  160. if (isset($matches['domain_port'])) {
  161. $this->setPort((int) $matches['domain_port']);
  162. }
  163. }
  164. if (self::ADDRESS_IPV6 == $this->_addressType &&
  165. (
  166. !defined('STREAM_PF_INET6') ||
  167. (function_exists('socket_create') && !defined('AF_INET6'))
  168. )
  169. ) {
  170. throw new Exception(
  171. 'IPv6 support has been disabled from PHP, we cannot use ' .
  172. 'the %s URI.',
  173. 1,
  174. $uri
  175. );
  176. }
  177. return;
  178. }
  179. /**
  180. * Set the port.
  181. *
  182. * @param int $port Port.
  183. * @return int
  184. * @throws \Hoa\Socket\Exception
  185. */
  186. protected function setPort($port)
  187. {
  188. if ($port < 0) {
  189. throw new Exception(
  190. 'Port must be greater or equal than zero, given %d.',
  191. 2,
  192. $port
  193. );
  194. }
  195. $old = $this->_port;
  196. $this->_port = $port;
  197. return $old;
  198. }
  199. /**
  200. * Set the transport.
  201. *
  202. * @param string $transport Transport (TCP, UDP etc.).
  203. * @return string
  204. * @throws \Hoa\Socket\Exception
  205. */
  206. protected function setTransport($transport)
  207. {
  208. $transport = strtolower($transport);
  209. if (false === Transport::exists($transport)) {
  210. throw new Exception(
  211. 'Transport %s is not enabled on this machin.',
  212. 3,
  213. $transport
  214. );
  215. }
  216. $old = $this->_transport;
  217. $this->_transport = $transport;
  218. return $old;
  219. }
  220. /**
  221. * Get the address.
  222. *
  223. * @return string
  224. */
  225. public function getAddress()
  226. {
  227. return $this->_address;
  228. }
  229. /**
  230. * Get the address type.
  231. *
  232. * @return int
  233. */
  234. public function getAddressType()
  235. {
  236. return $this->_addressType;
  237. }
  238. /**
  239. * Check if a port was declared.
  240. *
  241. * @return string
  242. */
  243. public function hasPort()
  244. {
  245. return -1 != $this->getPort();
  246. }
  247. /**
  248. * Get the port.
  249. *
  250. * @return int
  251. */
  252. public function getPort()
  253. {
  254. return $this->_port;
  255. }
  256. /**
  257. * Check if a transport was declared.
  258. *
  259. * @return bool
  260. */
  261. public function hasTransport()
  262. {
  263. return null !== $this->getTransport();
  264. }
  265. /**
  266. * Get the transport.
  267. *
  268. * @return string
  269. */
  270. public function getTransport()
  271. {
  272. return $this->_transport;
  273. }
  274. /**
  275. * Check if the socket is secured or not.
  276. *
  277. * @return bool
  278. */
  279. public function isSecured()
  280. {
  281. return $this->_secured;
  282. }
  283. /**
  284. * Get a string that represents the socket address.
  285. *
  286. * @return string
  287. */
  288. public function __toString()
  289. {
  290. $out = null;
  291. if (true === $this->hasTransport()) {
  292. $out .= $this->getTransport() . '://';
  293. }
  294. if (true === $this->hasPort()) {
  295. if (self::ADDRESS_IPV6 === $this->getAddressType()) {
  296. $out .= '[' . $this->getAddress() . ']';
  297. } else {
  298. $out .= $this->getAddress();
  299. }
  300. return $out . ':' . $this->getPort();
  301. }
  302. return $out . $this->getAddress();
  303. }
  304. }
  305. /**
  306. * Flex entity.
  307. */
  308. Consistency::flexEntity('Hoa\Socket\Socket');