Advertisement
Guest User

Untitled

a guest
Dec 2nd, 2016
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.88 KB | None | 0 0
  1. <?php
  2. declare(ticks=1);
  3. namespace PAMIClientImpl;
  4.  
  5. use PAMIMessageActionEventsAction;
  6. use PAMIMessageOutgoingMessage;
  7. use PAMIMessageMessage;
  8. use PAMIMessageIncomingMessage;
  9. use PAMIMessageActionLoginAction;
  10. use PAMIMessageActionLogoffAction;
  11. use PAMIMessageResponseResponseMessage;
  12. use PAMIMessageEventEventMessage;
  13. use PAMIMessageEventFactoryImplEventFactoryImpl;
  14. use PAMIListenerIEventListener;
  15. use PAMIClientExceptionClientException;
  16. use PAMIClientIClient;
  17.  
  18.  
  19.  
  20. class ClientImpl implements IClient
  21. {
  22.  
  23. private $_logger;
  24.  
  25. /**
  26. * Hostname
  27. * @var string
  28. */
  29. private $_host;
  30.  
  31. /**
  32. * TCP Port.
  33. * @var integer
  34. */
  35. private $_port;
  36.  
  37. /**
  38. * Username
  39. * @var string
  40. */
  41. private $_user;
  42.  
  43. /**
  44. * Password
  45. * @var string
  46. */
  47. private $_pass;
  48.  
  49. /**
  50. * Connection timeout, in seconds.
  51. * @var integer
  52. */
  53. private $_cTimeout;
  54.  
  55. /**
  56. * Connection scheme, like tcp:// or tls://
  57. * @var string
  58. */
  59. private $_scheme;
  60.  
  61. /**
  62. * Event factory.
  63. * @var EventFactoryImpl
  64. */
  65. private $_eventFactory;
  66.  
  67. /**
  68. * R/W timeout, in milliseconds.
  69. * @var integer
  70. */
  71. private $_rTimeout;
  72.  
  73. /**
  74. * Our stream socket resource.
  75. * @var resource
  76. */
  77. private $_socket;
  78.  
  79. /**
  80. * Our stream context resource.
  81. * @var resource
  82. */
  83. private $_context;
  84.  
  85. /**
  86. * Our event listeners
  87. * @var IEventListener[]
  88. */
  89. private $_eventListeners;
  90.  
  91. /**
  92. * The send queue
  93. * @var OutgoingMessage[]
  94. */
  95. private $_outgoingQueue;
  96.  
  97. /**
  98. * The receiving queue.
  99. * @var IncomingMessage[]
  100. */
  101. private $_incomingQueue;
  102.  
  103. /**
  104. * Our current received message. May be incomplete, will be completed
  105. * eventually with an EOM.
  106. * @var string
  107. */
  108. private $_currentProcessingMessage;
  109.  
  110. /**
  111. * This should not happen. Asterisk may send responses without a
  112. * corresponding ActionId.
  113. * @var string
  114. */
  115. private $_lastActionId;
  116.  
  117. /**
  118. * Opens a tcp connection to ami.
  119. *
  120. * @throws PAMIClientExceptionClientException
  121. * @return void
  122. */
  123. public function open()
  124. {
  125. $cString = $this->_scheme . $this->_host . ':' . $this->_port;
  126. $this->_context = stream_context_create();
  127. $errno = 0;
  128. $errstr = '';
  129. $this->_socket = @stream_socket_client(
  130. $cString, $errno, $errstr,
  131. $this->_cTimeout, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $this->_context
  132. );
  133. if ($this->_socket === false) {
  134. throw new ClientException('Error connecting to ami: ' . $errstr);
  135. }
  136. $msg = new LoginAction($this->_user, $this->_pass);
  137. $id = @stream_get_line($this->_socket, 1024, Message::EOL);
  138. if (strstr($id, 'Asterisk') === false) {
  139. throw new ClientException('Unknown peer. Is this an ami?: ' . $id);
  140. }
  141. $response = $this->send($msg);
  142. if (!$response->isSuccess()) {
  143. throw new ClientException('Could not connect: ' . $response->getMessage());
  144. }
  145.  
  146. /*add setfilter*/
  147. $filter = new EventsAction(array('call'));
  148. $response = $this->send($filter);
  149. if (!$response->isSuccess()) {
  150. throw new ClientException('Not set event filter on open: ' . $response->getMessage());
  151. }
  152.  
  153. @stream_set_blocking($this->_socket, 0);
  154. $this->_currentProcessingMessage = '';
  155. //register_tick_function(array($this, 'process'));
  156. if ($this->_logger->isDebugEnabled()) {
  157. $this->_logger->debug('Logged in successfully to ami.');
  158. }
  159. }
  160.  
  161. /**
  162. * Registers the given listener so it can receive events. Returns the generated
  163. * id for this new listener. You can pass in a an IEventListener, a Closure,
  164. * and an array containing the object and name of the method to invoke. Can specify
  165. * an optional predicate to invoke before calling the callback.
  166. *
  167. * @param mixed $listener
  168. * @param Closure|null $predicate
  169. *
  170. * @return string
  171. */
  172. public function registerEventListener($listener, $predicate = null)
  173. {
  174. $id = uniqid('PamiListener');
  175. $this->_eventListeners[$id] = array($listener, $predicate);
  176. return $id;
  177. }
  178.  
  179. /**
  180. * Unregisters an event listener.
  181. *
  182. * @param string $id The id returned by registerEventListener.
  183. *
  184. * @return void
  185. */
  186. public function unregisterEventListener($id)
  187. {
  188. if (isset($this->_eventListeners[$id])) {
  189. unset($this->_eventListeners[$id]);
  190. }
  191. }
  192.  
  193. /**
  194. * Reads a complete message over the stream until EOM.
  195. *
  196. * @return string
  197. */
  198. protected function getMessages()
  199. {
  200. $msgs = array();
  201.  
  202. // Read something.
  203. $read = @fread($this->_socket, 65535);
  204. if ($read === false || @feof($this->_socket)) {
  205. echo "empty data";
  206. throw new ClientException('Error reading');
  207. }
  208.  
  209.  
  210.  
  211. $this->_currentProcessingMessage .= $read;
  212.  
  213.  
  214. echo $this->_currentProcessingMessage.PHP_EOL;
  215. echo stream_get_meta_data($this->_socket);
  216.  
  217. // If we have a complete message, then return it. Save the rest for
  218. // later.
  219. while (($marker = strpos($this->_currentProcessingMessage, Message::EOM))) {
  220. $msg = substr($this->_currentProcessingMessage, 0, $marker);
  221. $this->_currentProcessingMessage = substr(
  222. $this->_currentProcessingMessage, $marker + strlen(Message::EOM)
  223. );
  224. $msgs[] = $msg;
  225. }
  226. return $msgs;
  227. }
  228.  
  229. /**
  230. * Main processing loop. Also called from send(), you should call this in
  231. * your own application in order to continue reading events and responses
  232. * from ami.
  233. */
  234. public function process()
  235. {
  236. $msgs = $this->getMessages();
  237. foreach ($msgs as $aMsg) {
  238. if ($this->_logger->isDebugEnabled()) {
  239. $this->_logger->debug(
  240. '------ Received: ------ ' . "n" . $aMsg . "nn"
  241. );
  242. }
  243. $resPos = strpos($aMsg, 'Response:');
  244. $evePos = strpos($aMsg, 'Event:');
  245. if (($resPos !== false) && (($resPos < $evePos) || $evePos === false)) {
  246. $response = $this->_messageToResponse($aMsg);
  247. $this->_incomingQueue[$response->getActionId()] = $response;
  248. } else if ($evePos !== false) {
  249. $event = $this->_messageToEvent($aMsg);
  250. $response = $this->findResponse($event);
  251. if ($response === false || $response->isComplete()) {
  252. $this->dispatch($event);
  253. } else {
  254. $response->addEvent($event);
  255. }
  256. } else {
  257. // broken ami.. sending a response with events without
  258. // Event and ActionId
  259. $bMsg = 'Event: ResponseEvent' . "rn";
  260. $bMsg .= 'ActionId: ' . $this->_lastActionId . "rn" . $aMsg;
  261. $event = $this->_messageToEvent($bMsg);
  262. $response = $this->findResponse($event);
  263. $response->addEvent($event);
  264. }
  265. if ($this->_logger->isDebugEnabled()) {
  266. $this->_logger->debug('----------------');
  267. }
  268. }
  269. }
  270.  
  271. /**
  272. * Tries to find an associated response for the given message.
  273. *
  274. * @param IncomingMessage $message Message sent by asterisk.
  275. *
  276. * @return PAMIMessageResponseResponseMessage
  277. */
  278. protected function findResponse(IncomingMessage $message)
  279. {
  280. $actionId = $message->getActionId();
  281. if (isset($this->_incomingQueue[$actionId])) {
  282. return $this->_incomingQueue[$actionId];
  283. }
  284. return false;
  285. }
  286.  
  287. /**
  288. * Dispatchs the incoming message to a handler.
  289. *
  290. * @param PAMIMessageIncomingMessage $message Message to dispatch.
  291. *
  292. * @return void
  293. */
  294. protected function dispatch(IncomingMessage $message)
  295. {
  296. foreach ($this->_eventListeners as $data) {
  297. $listener = $data[0];
  298. $predicate = $data[1];
  299. if ($predicate !== null && !$predicate($message)) {
  300. continue;
  301. }
  302. if ($listener instanceof Closure) {
  303. $listener($message);
  304. } else if (is_array($listener)) {
  305. $listener[0]->$listener[1]($message);
  306. } else {
  307. $listener->handle($message);
  308. }
  309. }
  310. }
  311.  
  312. /**
  313. * Returns a ResponseMessage from a raw string that came from asterisk.
  314. *
  315. * @param string $msg Raw string.
  316. *
  317. * @return PAMIMessageResponseResponseMessage
  318. */
  319. private function _messageToResponse($msg)
  320. {
  321. $response = new ResponseMessage($msg);
  322. $actionId = $response->getActionId();
  323. if ($actionId === null) {
  324. $actionId = $this->_lastActionId;
  325. $response->setActionId($this->_lastActionId);
  326. }
  327. return $response;
  328. }
  329.  
  330. /**
  331. * Returns a EventMessage from a raw string that came from asterisk.
  332. *
  333. * @param string $msg Raw string.
  334. *
  335. * @return PAMIMessageEventEventMessage
  336. */
  337. private function _messageToEvent($msg)
  338. {
  339. return $this->_eventFactory->createFromRaw($msg);
  340. }
  341.  
  342. /**
  343. * Returns a message (response) related to the given message. This uses
  344. * the ActionID tag (key).
  345. *
  346. * @todo not suitable for multithreaded applications.
  347. *
  348. * @return PAMIMessageIncomingMessage
  349. */
  350. protected function getRelated(OutgoingMessage $message)
  351. {
  352. $ret = false;
  353. $id = $message->getActionID('ActionID');
  354. if (isset($this->_incomingQueue[$id])) {
  355. $response = $this->_incomingQueue[$id];
  356. if ($response->isComplete()) {
  357. unset($this->_incomingQueue[$id]);
  358. $ret = $response;
  359. }
  360. }
  361. return $ret;
  362. }
  363.  
  364. /**
  365. * Sends a message to ami.
  366. *
  367. * @param PAMIMessageOutgoingMessage $message Message to send.
  368. *
  369. * @see ClientImpl::send()
  370. * @throws PAMIClientExceptionClientException
  371. * @return PAMIMessageResponseResponseMessage
  372. */
  373. public function send(OutgoingMessage $message)
  374. {
  375. $messageToSend = $message->serialize();
  376. $length = strlen($messageToSend);
  377. if ($this->_logger->isDebugEnabled()) {
  378. $this->_logger->debug(
  379. '------ Sending: ------ ' . "n" . $messageToSend . '----------'
  380. );
  381. }
  382. $this->_lastActionId = $message->getActionId();
  383. if (@fwrite($this->_socket, $messageToSend) < $length) {
  384. throw new ClientException('Could not send message');
  385. }
  386. $read = 0;
  387. while($read <= $this->_rTimeout) {
  388. $this->process();
  389. $response = $this->getRelated($message);
  390. if ($response != false) {
  391. $this->_lastActionId = false;
  392. return $response;
  393. }
  394. usleep(1000); // 1ms delay
  395. if ($this->_rTimeout > 0) {
  396. $read++;
  397. }
  398. }
  399. throw new ClientException('Read timeout');
  400. }
  401.  
  402. /**
  403. * Closes the connection to ami.
  404. *
  405. * @return void
  406. */
  407. public function close()
  408. {
  409. if ($this->_logger->isDebugEnabled()) {
  410. $this->_logger->debug('Closing connection to asterisk.');
  411. }
  412. @stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR);
  413. }
  414.  
  415. /**
  416. * Constructor.
  417. *
  418. * @param string[] $options Options for ami client.
  419. *
  420. * @return void
  421. */
  422. public function __construct(array $options)
  423. {
  424. if (isset($options['log4php.properties'])) {
  425. Logger::configure($options['log4php.properties']);
  426. }
  427. $this->_logger = Logger::getLogger('Pami.ClientImpl');
  428. $this->_host = $options['host'];
  429. $this->_port = intval($options['port']);
  430. $this->_user = $options['username'];
  431. $this->_pass = $options['secret'];
  432. $this->_cTimeout = $options['connect_timeout'];
  433. $this->_rTimeout = $options['read_timeout'];
  434. $this->_scheme = isset($options['scheme']) ? $options['scheme'] : 'tcp://';
  435. $this->_eventListeners = array();
  436. $this->_eventFactory = new EventFactoryImpl();
  437. $this->_incomingQueue = array();
  438. $this->_lastActionId = false;
  439. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement