<?php namespace App\Library; class SMPP { public $socket; public $seq = 0; public $debug = 0; public $data_coding = 0; public $timeout = 2; /** * @param $id * @param $data * * @return array|false|string */ function send_pdu($id, $data) { if ($this->socket == false) { return "Send PDU: Connection closed!"; } // increment sequence $this->seq += 1; // PDU = PDU_header + PDU_content $pdu = pack('NNNN', strlen($data) + 16, $id, 0, $this->seq).$data; // send PDU fputs($this->socket, $pdu); // Get response length $data = fread($this->socket, 4); if ($data == false) { return "Send PDU: Connection closed!"; } $tmp = unpack('Nlength', $data); $command_length = $tmp['length']; if ($command_length < 12) { return false; } // Get response $data = fread($this->socket, $command_length - 4); $pdu = unpack('Nid/Nstatus/Nseq', $data); if ($this->debug) { print "\n< R PDU (id,status,seq): ".join(" ", $pdu); } return $pdu; } /** * @param $host * @param $port * @param $system_id * @param $password * * @return bool|string */ function open($host, $port, $system_id, $password) { // Open the socket $this->socket = @fsockopen($host, $port, $errno, $err_str, $this->timeout); if ($this->socket == false) { return "$err_str ($errno)<br />"; } if (function_exists('stream_set_timeout')) { stream_set_timeout($this->socket, $this->timeout); } // function exists for php4.3+ if ($this->debug) { print "\n> Connected"; } // Send Bind operation $data = sprintf("%s\0%s\0", $system_id, $password); // system_id, password $data .= sprintf("%s\0%c", "", 0x34); // system_type, interface_version $data .= sprintf("%c%c%s\0", 5, 0, ""); // addr_ton, addr_npi, address_range $ret = $this->send_pdu(2, $data); if ($this->debug) { print "\n> Bind done!"; } if (is_array($ret) && array_key_exists('status', $ret)){ return ($ret['status'] == 0); } print "\n> Please check your server firewall"; return false; } /** * @param $source_addr * @param $destination_addr * @param $short_message * @param string $optional * * @return bool */ function submit_sm($source_addr, $destination_addr, $short_message, $optional = ''): bool { $data = sprintf("%s\0", ""); // service_type $data .= sprintf("%c%c%s\0", 5, 0, $source_addr); // source_addr_ton, source_addr_npi, source_addr $data .= sprintf("%c%c%s\0", 1, 1, $destination_addr); // dest_addr_ton, dest_addr_npi, destination_addr $data .= sprintf("%c%c%c", 0, 0, 0); // esm_class, protocol_id, priority_flag $data .= sprintf("%s\0%s\0", "", ""); // schedule_delivery_time, validity_period $data .= sprintf("%c%c", 0, 0); // registered_delivery, replace_if_present_flag $data .= sprintf("%c%c", $this->data_coding, 0); // data_coding, sm_default_msg_id $data .= sprintf("%c%s", strlen($short_message), $short_message); // sm_length, short_message $data .= $optional; $ret = $this->send_pdu(4, $data); if (isset($ret['status'])) { return ($ret['status'] == 0); } return false; } /** * @return bool|string */ function close() { if ($this->socket == false) { return "Send PDU: Connection closed!"; } $this->send_pdu(6, ""); fclose($this->socket); return true; } /** * @param $source_addr * @param $destination_addr * @param $short_message * @param int $utf * @param int $flash * * @return bool */ function send_long($source_addr, $destination_addr, $short_message, $utf = 0, $flash = 0): bool { if ($utf) { $this->data_coding = 0x08; } if ($flash) { $this->data_coding = $this->data_coding | 0x10; } $size = strlen($short_message); if ($utf) { $size += 20; } if ($size < 160) { // Only one part :) $this->submit_sm($source_addr, $destination_addr, $short_message); } else { // Multipart $sar_msg_ref_num = rand(1, 255); $sar_total_segments = ceil(strlen($short_message) / 130); for ($sar_segment_seq_num = 1; $sar_segment_seq_num <= $sar_total_segments; $sar_segment_seq_num++) { $part = substr($short_message, 0, 130); $short_message = substr($short_message, 130); $optional = pack('nnn', 0x020C, 2, $sar_msg_ref_num); $optional .= pack('nnc', 0x020E, 1, $sar_total_segments); $optional .= pack('nnc', 0x020F, 1, $sar_segment_seq_num); if ($this->submit_sm($source_addr, $destination_addr, $part, $optional) === false) { return false; } } } return true; } }