Check disconnect clients in PHP socket server - php

#!/usr/local/bin/php -q
<?
set_time_limit (0);
$address = '192.168.0.201';
$port = 1077;
$max_clients = 10;
$clients = Array();
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sock, $address, $port) or die('fail.');
socket_listen($sock);
while (true) {
$read[0] = $sock;
for ($i = 0; $i < $max_clients; $i++)
{
if ($client[$i]['sock'] != null)
$read[$i + 1] = $client[$i]['sock'] ;
}
$write=NULL;
$exceptions=NULL;
$ready = socket_select($read,$write,$exceptions,null);
if (in_array($sock, $read)) {
for ($i = 0; $i < $max_clients; $i++)
{
if ($client[$i]['sock'] == null) {
$client[$i]['sock'] = socket_accept($sock);
break;
}
elseif ($i == $max_clients - 1)
print ("many clients");
}
if (--$ready <= 0)
continue;
}
for ($i = 0; $i < $max_clients; $i++)
{
if (in_array($client[$i]['sock'] , $read))
{
$input = socket_read($client[$i]['sock'] , 1024);
if ($input == null) {
unset($client[$i]);
}
$n = trim($input);
if ($input == 'exit') {
socket_close($client[$i]['sock']);
} elseif ($input) {
$host = 'localhost';
$uname = 'root';
$pwd = 'taek0526';
$db = 'InputTest';
$con = mysql_connect($host,$uname,$pwd) or die("connection failed");
mysql_select_db($db,$con) or die("db selection failed");
mysql_query("set names utf8");
$data = explode(" ", $input);
mysql_query("INSERT INTO `test`(`data1`, `data2`) VALUES ('".$data[0]."', '".$data[1]."')");
mysql_close($con);
}
} else {
}
}
}
socket_close($sock);
?>
This is sample code about server.
When i test this code, have a problem. If client close program with out send "exit" client can not connect again so, I kill server process and restart; after that, client can connect again.
I think remain some data about previous connection.
How to check disconnect clients?
And then, how to remove data about disconnect clients?
for ($i = 0; $i < $max_clients; $i++) {
if( Check disconnect ){
disconnect work
}
}
I want to make code and add like this, but i don't no php function of socket.

How to check disconnect clients?
You made this check already with this line:
if ($input == null) {
You just forgot to also insert
socket_close($client[$i]['sock']);
before
unset($client[$i]);
or you might want to combine
if ($input == null or trim($input) == 'exit')
{
socket_close($client[$i]['sock']);
unset($client[$i]);
And then, how to remove data about disconnect clients?
In addition to the above unset($client[$i]) you have to replace
$read[0] = $sock;
with
$read = Array($sock);
to clear array elements from the previous loop cycle.

Related

how to make php websocket server accept connect with wss:// protocol

I am using this php class to make websocket server and i succeed to connect to websocket server using ws:// protocol but I need to connect using wss://
I am using this websocket server to build chat script and i have not informations about another websocket services like websocket.io node.js and other libraries i want to create the websocket server in pure php only
the php class is below
<?php
class PHPWebSocket
{
// maximum amount of clients that can be connected at one time
const WS_MAX_CLIENTS = 100;
// maximum amount of clients that can be connected at one time on the same IP v4 address
const WS_MAX_CLIENTS_PER_IP = 15;
// amount of seconds a client has to send data to the server, before a ping request is sent to the client,
// if the client has not completed the opening handshake, the ping request is skipped and the client connection is closed
const WS_TIMEOUT_RECV = 10;
// amount of seconds a client has to reply to a ping request, before the client connection is closed
const WS_TIMEOUT_PONG = 5;
// the maximum length, in bytes, of a frame's payload data (a message consists of 1 or more frames), this is also internally limited to 2,147,479,538
const WS_MAX_FRAME_PAYLOAD_RECV = 100000;
// the maximum length, in bytes, of a message's payload data, this is also internally limited to 2,147,483,647
const WS_MAX_MESSAGE_PAYLOAD_RECV = 500000;
// internal
const WS_FIN = 128;
const WS_MASK = 128;
const WS_OPCODE_CONTINUATION = 0;
const WS_OPCODE_TEXT = 1;
const WS_OPCODE_BINARY = 2;
const WS_OPCODE_CLOSE = 8;
const WS_OPCODE_PING = 9;
const WS_OPCODE_PONG = 10;
const WS_PAYLOAD_LENGTH_16 = 126;
const WS_PAYLOAD_LENGTH_63 = 127;
const WS_READY_STATE_CONNECTING = 0;
const WS_READY_STATE_OPEN = 1;
const WS_READY_STATE_CLOSING = 2;
const WS_READY_STATE_CLOSED = 3;
const WS_STATUS_NORMAL_CLOSE = 1000;
const WS_STATUS_GONE_AWAY = 1001;
const WS_STATUS_PROTOCOL_ERROR = 1002;
const WS_STATUS_UNSUPPORTED_MESSAGE_TYPE = 1003;
const WS_STATUS_MESSAGE_TOO_BIG = 1004;
const WS_STATUS_TIMEOUT = 3000;
// global vars
public $wsClients = array();
public $wsRead = array();
public $wsClientCount = 0;
public $wsClientIPCount = array();
public $wsOnEvents = array();
// server state functions
function wsStartServer($host, $port) {
if (isset($this->wsRead[0])) return false;
if (!$this->wsRead[0] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
return false;
}
if (!socket_set_option($this->wsRead[0], SOL_SOCKET, SO_REUSEADDR, 1)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_bind($this->wsRead[0], $host, $port)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_listen($this->wsRead[0], 10)) {
socket_close($this->wsRead[0]);
return false;
}
$write = array();
$except = array();
$nextPingCheck = time() + 1;
while (isset($this->wsRead[0])) {
$changed = $this->wsRead;
$result = socket_select($changed, $write, $except, 1);
if ($result === false) {
socket_close($this->wsRead[0]);
return false;
}
elseif ($result > 0) {
foreach ($changed as $clientID => $socket) {
if ($clientID != 0) {
// client socket changed
$buffer = '';
$bytes = #socket_recv($socket, $buffer, 4096, 0);
if ($bytes === false) {
// error on recv, remove client socket (will check to send close frame)
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
elseif ($bytes > 0) {
// process handshake or frame(s)
if (!$this->wsProcessClient($clientID, $buffer, $bytes)) {
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
}
else {
// 0 bytes received from client, meaning the client closed the TCP connection
$this->wsRemoveClient($clientID);
}
}
else {
// listen socket changed
$client = socket_accept($this->wsRead[0]);
if ($client !== false) {
// fetch client IP as integer
$clientIP = '';
$result = socket_getpeername($client, $clientIP);
$clientIP = ip2long($clientIP);
if ($result !== false && $this->wsClientCount < self::WS_MAX_CLIENTS && (!isset($this->wsClientIPCount[$clientIP]) || $this->wsClientIPCount[$clientIP] < self::WS_MAX_CLIENTS_PER_IP)) {
$this->wsAddClient($client, $clientIP);
}
else {
socket_close($client);
}
}
}
}
}
if (time() >= $nextPingCheck) {
$this->wsCheckIdleClients();
$nextPingCheck = time() + 1;
}
}
return true; // returned when wsStopServer() is called
}
function wsStopServer() {
// check if server is not running
if (!isset($this->wsRead[0])) return false;
// close all client connections
foreach ($this->wsClients as $clientID => $client) {
// if the client's opening handshake is complete, tell the client the server is 'going away'
if ($client[2] != self::WS_READY_STATE_CONNECTING) {
$this->wsSendClientClose($clientID, self::WS_STATUS_GONE_AWAY);
}
socket_close($client[0]);
}
// close the socket which listens for incoming clients
socket_close($this->wsRead[0]);
// reset variables
$this->wsRead = array();
$this->wsClients = array();
$this->wsClientCount = 0;
$this->wsClientIPCount = array();
return true;
}
// client timeout functions
function wsCheckIdleClients() {
$time = time();
foreach ($this->wsClients as $clientID => $client) {
if ($client[2] != self::WS_READY_STATE_CLOSED) {
// client ready state is not closed
if ($client[4] !== false) {
// ping request has already been sent to client, pending a pong reply
if ($time >= $client[4] + self::WS_TIMEOUT_PONG) {
// client didn't respond to the server's ping request in self::WS_TIMEOUT_PONG seconds
$this->wsSendClientClose($clientID, self::WS_STATUS_TIMEOUT);
$this->wsRemoveClient($clientID);
}
}
elseif ($time >= $client[3] + self::WS_TIMEOUT_RECV) {
// last data was received >= self::WS_TIMEOUT_RECV seconds ago
if ($client[2] != self::WS_READY_STATE_CONNECTING) {
// client ready state is open or closing
$this->wsClients[$clientID][4] = time();
$this->wsSendClientMessage($clientID, self::WS_OPCODE_PING, '');
}
else {
// client ready state is connecting
$this->wsRemoveClient($clientID);
}
}
}
}
}
// client existence functions
function wsAddClient($socket, $clientIP) {
// increase amount of clients connected
$this->wsClientCount++;
// increase amount of clients connected on this client's IP
if (isset($this->wsClientIPCount[$clientIP])) {
$this->wsClientIPCount[$clientIP]++;
}
else {
$this->wsClientIPCount[$clientIP] = 1;
}
// fetch next client ID
$clientID = $this->wsGetNextClientID();
// store initial client data
$this->wsClients[$clientID] = array($socket, '', self::WS_READY_STATE_CONNECTING, time(), false, 0, $clientIP, false, 0, '', 0, 0);
// store socket - used for socket_select()
$this->wsRead[$clientID] = $socket;
}
function wsRemoveClient($clientID) {
// fetch close status (which could be false), and call wsOnClose
$closeStatus = $this->wsClients[$clientID][5];
if ( array_key_exists('close', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['close'] as $func )
$func($clientID, $closeStatus);
// close socket
$socket = $this->wsClients[$clientID][0];
socket_close($socket);
// decrease amount of clients connected on this client's IP
$clientIP = $this->wsClients[$clientID][6];
if ($this->wsClientIPCount[$clientIP] > 1) {
$this->wsClientIPCount[$clientIP]--;
}
else {
unset($this->wsClientIPCount[$clientIP]);
}
// decrease amount of clients connected
$this->wsClientCount--;
// remove socket and client data from arrays
unset($this->wsRead[$clientID], $this->wsClients[$clientID]);
}
// client data functions
function wsGetNextClientID() {
$i = 1; // starts at 1 because 0 is the listen socket
while (isset($this->wsRead[$i])) $i++;
return $i;
}
function wsGetClientSocket($clientID) {
return $this->wsClients[$clientID][0];
}
// client read functions
function wsProcessClient($clientID, &$buffer, $bufferLength) {
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_OPEN) {
// handshake completed
$result = $this->wsBuildClientFrame($clientID, $buffer, $bufferLength);
}
elseif ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CONNECTING) {
// handshake not completed
$result = $this->wsProcessClientHandshake($clientID, $buffer);
if ($result) {
$this->wsClients[$clientID][2] = self::WS_READY_STATE_OPEN;
if ( array_key_exists('open', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['open'] as $func )
$func($clientID);
}
}
else {
// ready state is set to closed
$result = false;
}
return $result;
}
function wsBuildClientFrame($clientID, &$buffer, $bufferLength) {
// increase number of bytes read for the frame, and join buffer onto end of the frame buffer
$this->wsClients[$clientID][8] += $bufferLength;
$this->wsClients[$clientID][9] .= $buffer;
// check if the length of the frame's payload data has been fetched, if not then attempt to fetch it from the frame buffer
if ($this->wsClients[$clientID][7] !== false || $this->wsCheckSizeClientFrame($clientID) == true) {
// work out the header length of the frame
$headerLength = ($this->wsClients[$clientID][7] <= 125 ? 0 : ($this->wsClients[$clientID][7] <= 65535 ? 2 : 8)) + 6;
// check if all bytes have been received for the frame
$frameLength = $this->wsClients[$clientID][7] + $headerLength;
if ($this->wsClients[$clientID][8] >= $frameLength) {
// check if too many bytes have been read for the frame (they are part of the next frame)
$nextFrameBytesLength = $this->wsClients[$clientID][8] - $frameLength;
if ($nextFrameBytesLength > 0) {
$this->wsClients[$clientID][8] -= $nextFrameBytesLength;
$nextFrameBytes = substr($this->wsClients[$clientID][9], $frameLength);
$this->wsClients[$clientID][9] = substr($this->wsClients[$clientID][9], 0, $frameLength);
}
// process the frame
$result = $this->wsProcessClientFrame($clientID);
// check if the client wasn't removed, then reset frame data
if (isset($this->wsClients[$clientID])) {
$this->wsClients[$clientID][7] = false;
$this->wsClients[$clientID][8] = 0;
$this->wsClients[$clientID][9] = '';
}
// if there's no extra bytes for the next frame, or processing the frame failed, return the result of processing the frame
if ($nextFrameBytesLength <= 0 || !$result) return $result;
// build the next frame with the extra bytes
return $this->wsBuildClientFrame($clientID, $nextFrameBytes, $nextFrameBytesLength);
}
}
return true;
}
function wsCheckSizeClientFrame($clientID) {
// check if at least 2 bytes have been stored in the frame buffer
if ($this->wsClients[$clientID][8] > 1) {
// fetch payload length in byte 2, max will be 127
$payloadLength = ord(substr($this->wsClients[$clientID][9], 1, 1)) & 127;
if ($payloadLength <= 125) {
// actual payload length is <= 125
$this->wsClients[$clientID][7] = $payloadLength;
}
elseif ($payloadLength == 126) {
// actual payload length is <= 65,535
if (substr($this->wsClients[$clientID][9], 3, 1) !== false) {
// at least another 2 bytes are set
$payloadLengthExtended = substr($this->wsClients[$clientID][9], 2, 2);
$array = unpack('na', $payloadLengthExtended);
$this->wsClients[$clientID][7] = $array['a'];
}
}
else {
// actual payload length is > 65,535
if (substr($this->wsClients[$clientID][9], 9, 1) !== false) {
// at least another 8 bytes are set
$payloadLengthExtended = substr($this->wsClients[$clientID][9], 2, 8);
// check if the frame's payload data length exceeds 2,147,483,647 (31 bits)
// the maximum integer in PHP is "usually" this number. More info: http://php.net/manual/en/language.types.integer.php
$payloadLengthExtended32_1 = substr($payloadLengthExtended, 0, 4);
$array = unpack('Na', $payloadLengthExtended32_1);
if ($array['a'] != 0 || ord(substr($payloadLengthExtended, 4, 1)) & 128) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// fetch length as 32 bit unsigned integer, not as 64 bit
$payloadLengthExtended32_2 = substr($payloadLengthExtended, 4, 4);
$array = unpack('Na', $payloadLengthExtended32_2);
// check if the payload data length exceeds 2,147,479,538 (2,147,483,647 - 14 - 4095)
// 14 for header size, 4095 for last recv() next frame bytes
if ($array['a'] > 2147479538) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// store frame payload data length
$this->wsClients[$clientID][7] = $array['a'];
}
}
// check if the frame's payload data length has now been stored
if ($this->wsClients[$clientID][7] !== false) {
// check if the frame's payload data length exceeds self::WS_MAX_FRAME_PAYLOAD_RECV
if ($this->wsClients[$clientID][7] > self::WS_MAX_FRAME_PAYLOAD_RECV) {
$this->wsClients[$clientID][7] = false;
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// check if the message's payload data length exceeds 2,147,483,647 or self::WS_MAX_MESSAGE_PAYLOAD_RECV
// doesn't apply for control frames, where the payload data is not internally stored
$controlFrame = (ord(substr($this->wsClients[$clientID][9], 0, 1)) & 8) == 8;
if (!$controlFrame) {
$newMessagePayloadLength = $this->wsClients[$clientID][11] + $this->wsClients[$clientID][7];
if ($newMessagePayloadLength > self::WS_MAX_MESSAGE_PAYLOAD_RECV || $newMessagePayloadLength > 2147483647) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
}
return true;
}
}
return false;
}
function wsProcessClientFrame($clientID) {
// store the time that data was last received from the client
$this->wsClients[$clientID][3] = time();
// fetch frame buffer
$buffer = &$this->wsClients[$clientID][9];
// check at least 6 bytes are set (first 2 bytes and 4 bytes for the mask key)
if (substr($buffer, 5, 1) === false) return false;
// fetch first 2 bytes of header
$octet0 = ord(substr($buffer, 0, 1));
$octet1 = ord(substr($buffer, 1, 1));
$fin = $octet0 & self::WS_FIN;
$opcode = $octet0 & 15;
$mask = $octet1 & self::WS_MASK;
if (!$mask) return false; // close socket, as no mask bit was sent from the client
// fetch byte position where the mask key starts
$seek = $this->wsClients[$clientID][7] <= 125 ? 2 : ($this->wsClients[$clientID][7] <= 65535 ? 4 : 10);
// read mask key
$maskKey = substr($buffer, $seek, 4);
$array = unpack('Na', $maskKey);
$maskKey = $array['a'];
$maskKey = array(
$maskKey >> 24,
($maskKey >> 16) & 255,
($maskKey >> 8) & 255,
$maskKey & 255
);
$seek += 4;
// decode payload data
if (substr($buffer, $seek, 1) !== false) {
$data = str_split(substr($buffer, $seek));
foreach ($data as $key => $byte) {
$data[$key] = chr(ord($byte) ^ ($maskKey[$key % 4]));
}
$data = implode('', $data);
}
else {
$data = '';
}
// check if this is not a continuation frame and if there is already data in the message buffer
if ($opcode != self::WS_OPCODE_CONTINUATION && $this->wsClients[$clientID][11] > 0) {
// clear the message buffer
$this->wsClients[$clientID][11] = 0;
$this->wsClients[$clientID][1] = '';
}
// check if the frame is marked as the final frame in the message
if ($fin == self::WS_FIN) {
// check if this is the first frame in the message
if ($opcode != self::WS_OPCODE_CONTINUATION) {
// process the message
return $this->wsProcessClientMessage($clientID, $opcode, $data, $this->wsClients[$clientID][7]);
}
else {
// increase message payload data length
$this->wsClients[$clientID][11] += $this->wsClients[$clientID][7];
// push frame payload data onto message buffer
$this->wsClients[$clientID][1] .= $data;
// process the message
$result = $this->wsProcessClientMessage($clientID, $this->wsClients[$clientID][10], $this->wsClients[$clientID][1], $this->wsClients[$clientID][11]);
// check if the client wasn't removed, then reset message buffer and message opcode
if (isset($this->wsClients[$clientID])) {
$this->wsClients[$clientID][1] = '';
$this->wsClients[$clientID][10] = 0;
$this->wsClients[$clientID][11] = 0;
}
return $result;
}
}
else {
// check if the frame is a control frame, control frames cannot be fragmented
if ($opcode & 8) return false;
// increase message payload data length
$this->wsClients[$clientID][11] += $this->wsClients[$clientID][7];
// push frame payload data onto message buffer
$this->wsClients[$clientID][1] .= $data;
// if this is the first frame in the message, store the opcode
if ($opcode != self::WS_OPCODE_CONTINUATION) {
$this->wsClients[$clientID][10] = $opcode;
}
}
return true;
}
function wsProcessClientMessage($clientID, $opcode, &$data, $dataLength) {
// check opcodes
if ($opcode == self::WS_OPCODE_PING) {
// received ping message
return $this->wsSendClientMessage($clientID, self::WS_OPCODE_PONG, $data);
}
elseif ($opcode == self::WS_OPCODE_PONG) {
// received pong message (it's valid if the server did not send a ping request for this pong message)
if ($this->wsClients[$clientID][4] !== false) {
$this->wsClients[$clientID][4] = false;
}
}
elseif ($opcode == self::WS_OPCODE_CLOSE) {
// received close message
if (substr($data, 1, 1) !== false) {
$array = unpack('na', substr($data, 0, 2));
$status = $array['a'];
}
else {
$status = false;
}
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING) {
// the server already sent a close frame to the client, this is the client's close frame reply
// (no need to send another close frame to the client)
$this->wsClients[$clientID][2] = self::WS_READY_STATE_CLOSED;
}
else {
// the server has not already sent a close frame to the client, send one now
$this->wsSendClientClose($clientID, self::WS_STATUS_NORMAL_CLOSE);
}
$this->wsRemoveClient($clientID);
}
elseif ($opcode == self::WS_OPCODE_TEXT || $opcode == self::WS_OPCODE_BINARY) {
if ( array_key_exists('message', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['message'] as $func )
$func($clientID, $data, $dataLength, $opcode == self::WS_OPCODE_BINARY);
}
else {
// unknown opcode
return false;
}
return true;
}
function wsProcessClientHandshake($clientID, &$buffer) {
// fetch headers and request line
$sep = strpos($buffer, "\r\n\r\n");
if (!$sep) return false;
$headers = explode("\r\n", substr($buffer, 0, $sep));
$headersCount = sizeof($headers); // includes request line
if ($headersCount < 1) return false;
// fetch request and check it has at least 3 parts (space tokens)
$request = &$headers[0];
$requestParts = explode(' ', $request);
$requestPartsSize = sizeof($requestParts);
if ($requestPartsSize < 3) return false;
// check request method is GET
if (strtoupper($requestParts[0]) != 'GET') return false;
// check request HTTP version is at least 1.1
$httpPart = &$requestParts[$requestPartsSize - 1];
$httpParts = explode('/', $httpPart);
if (!isset($httpParts[1]) || (float) $httpParts[1] < 1.1) return false;
// store headers into a keyed array: array[headerKey] = headerValue
$headersKeyed = array();
for ($i=1; $i<$headersCount; $i++) {
$parts = explode(':', $headers[$i]);
if (!isset($parts[1])) return false;
$headersKeyed[trim($parts[0])] = trim($parts[1]);
}
// check Host header was received
if (!isset($headersKeyed['Host'])) return false;
// check Sec-WebSocket-Key header was received and decoded value length is 16
if (!isset($headersKeyed['Sec-WebSocket-Key'])) return false;
$key = $headersKeyed['Sec-WebSocket-Key'];
if (strlen(base64_decode($key)) != 16) return false;
// check Sec-WebSocket-Version header was received and value is 7
if (!isset($headersKeyed['Sec-WebSocket-Version']) || (int) $headersKeyed['Sec-WebSocket-Version'] < 7) return false; // should really be != 7, but Firefox 7 beta users send 8
// work out hash to use in Sec-WebSocket-Accept reply header
$hash = base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
// build headers
$headers = array(
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: '.$hash
);
$headers = implode("\r\n", $headers)."\r\n\r\n";
// send headers back to client
$socket = $this->wsClients[$clientID][0];
$left = strlen($headers);
do {
$sent = #socket_send($socket, $headers, $left, 0);
if ($sent === false) return false;
$left -= $sent;
if ($sent > 0) $headers = substr($headers, $sent);
}
while ($left > 0);
return true;
}
// client write functions
function wsSendClientMessage($clientID, $opcode, $message) {
// check if client ready state is already closing or closed
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING || $this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSED) return true;
// fetch message length
$messageLength = strlen($message);
// set max payload length per frame
$bufferSize = 4096;
// work out amount of frames to send, based on $bufferSize
$frameCount = ceil($messageLength / $bufferSize);
if ($frameCount == 0) $frameCount = 1;
// set last frame variables
$maxFrame = $frameCount - 1;
$lastFrameBufferLength = ($messageLength % $bufferSize) != 0 ? ($messageLength % $bufferSize) : ($messageLength != 0 ? $bufferSize : 0);
// loop around all frames to send
for ($i=0; $i<$frameCount; $i++) {
// fetch fin, opcode and buffer length for frame
$fin = $i != $maxFrame ? 0 : self::WS_FIN;
$opcode = $i != 0 ? self::WS_OPCODE_CONTINUATION : $opcode;
$bufferLength = $i != $maxFrame ? $bufferSize : $lastFrameBufferLength;
// set payload length variables for frame
if ($bufferLength <= 125) {
$payloadLength = $bufferLength;
$payloadLengthExtended = '';
$payloadLengthExtendedLength = 0;
}
elseif ($bufferLength <= 65535) {
$payloadLength = self::WS_PAYLOAD_LENGTH_16;
$payloadLengthExtended = pack('n', $bufferLength);
$payloadLengthExtendedLength = 2;
}
else {
$payloadLength = self::WS_PAYLOAD_LENGTH_63;
$payloadLengthExtended = pack('xxxxN', $bufferLength); // pack 32 bit int, should really be 64 bit int
$payloadLengthExtendedLength = 8;
}
// set frame bytes
$buffer = pack('n', (($fin | $opcode) << 8) | $payloadLength) . $payloadLengthExtended . substr($message, $i*$bufferSize, $bufferLength);
// send frame
$socket = $this->wsClients[$clientID][0];
$left = 2 + $payloadLengthExtendedLength + $bufferLength;
do {
$sent = #socket_send($socket, $buffer, $left, 0);
if ($sent === false) return false;
$left -= $sent;
if ($sent > 0) $buffer = substr($buffer, $sent);
}
while ($left > 0);
}
return true;
}
function wsSendClientClose($clientID, $status=false) {
// check if client ready state is already closing or closed
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING || $this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSED) return true;
// store close status
$this->wsClients[$clientID][5] = $status;
// send close frame to client
$status = $status !== false ? pack('n', $status) : '';
$this->wsSendClientMessage($clientID, self::WS_OPCODE_CLOSE, $status);
// set client ready state to closing
$this->wsClients[$clientID][2] = self::WS_READY_STATE_CLOSING;
}
// client non-internal functions
function wsClose($clientID) {
return $this->wsSendClientClose($clientID, self::WS_STATUS_NORMAL_CLOSE);
}
function wsSend($clientID, $message, $binary=false) {
return $this->wsSendClientMessage($clientID, $binary ? self::WS_OPCODE_BINARY : self::WS_OPCODE_TEXT, $message);
}
function log( $message )
{
echo date('Y-m-d H:i:s: ') . $message . "\n";
}
function bind( $type, $func )
{
if ( !isset($this->wsOnEvents[$type]) )
$this->wsOnEvents[$type] = array();
$this->wsOnEvents[$type][] = $func;
}
function unbind( $type='' )
{
if ( $type ) unset($this->wsOnEvents[$type]);
else $this->wsOnEvents = array();
}
}
?>

Connection lost during long time process to save records in database sqlite3 codeigniter

I have a heavy process which produce lots of records (>1000000) that should be save to the sqlite3 database in codeigniter.
The setting to connect to database in runtime is like this
$config['hostname'] = '';
$config['username'] = '';
$config['password'] = '';
$config['database'] = './application/database/' . $database_name;
$config['dbdriver'] = 'sqlite3';
$config['dbprefix'] = '';
$config['pconnect'] = false;
$config['db_debug'] = true;
$config['autoinit'] = false;
$config['cache_on'] = false;
$config['cachedir'] = '';
$config['stricton'] = false;
$config['char_set'] = 'utf8';
$config['dbcollat'] = 'utf8_general_ci';
$config['save_queries'] = true;
$config['failover'] = array();
my code in CI_Controller is
$counter = 0;
$this->load->model('db_model');
for ($j = 0; $j < $MAX_Y; $j++) {
for ($i = 0; $i < $MAX_X; $i++) {
// some process .....
// prepare $values
if ($counter > 20000) {
$this->db_model->insert($values, $config);
$counter = 0;
unset($values);
$values = array();
} else {
$counter++;
}
}
}
if ($counter > 0) {
$this->db_model->insert($values, $config);
}
my code in db_model is
class Db_model extends CI_Model
{
public function __construct()
{
parent::__construct();
set_time_limit(10000);
}
public function insert($data, $config)
{
$DB = null;
$DB = $this->load->database($config, TRUE);
$DB->initialize();
// $DB->busyTimeout(72000000);
$DB->trans_start();
$DB->insert_batch('tbl_name', $data);
$DB->trans_complete();
$DB->close();
}
}
During the saving records into table in sqlite3 database suddenly it stop writing the rest of records which is remain and table does not have all records which should have.
I think some thing is wrong with connection time out, and i should change some thing related to connection time out. but i did not find a good answer for this issue after couple of weeks.
Do like this: $db['default']['options'] = array(PDO::ATTR_TIMEOUT => 50000);
In config file: (application/config/database.php)
Refer this answer
This may be caused because of your code which is performed before inserting the data, which cause the server connection to break to save the resources.
The best way to do it is just close the connection and reconnect DB manually before the insert query using this:
$this->db->close();
$this->db->initialize();
Or
$this->db->reconnect();
Check: Reconnecting / Keeping the Connection Alive for reference.
You put the set_time_limit(10000); inside of the model. But you run over a loop in the CI_Controller, and I guess that the inserts them self are not long.
The time out won't happen in the model, it will happen in the controller. So try to change the CI_Controller like this:
$counter = 0;
$this->load->model('db_model');
set_time_limit(10000); //set time limit to the longest run of this script
for ($j = 0; $j < $MAX_Y; $j++) {
for ($i = 0; $i < $MAX_X; $i++) {
// some process .....
// prepare $values
if ($counter > 20000) {
$this->db_model->insert($values, $config);
$counter = 0;
unset($values);
$values = array();
} else {
$counter++;
}
}
}
if ($counter > 0) {
$this->db_model->insert($values, $config);
}
I got super tired of PHP timeouts and I started writing
"restart-able" programs. The basics...
$step=(int)$_SERVER["QUERY_STRING"];
switch($step) {
case 0:
/* SETUP */
redirect($_SERVER["PHP_SELF"]."?1";
case 1:
/* DO SOME WORK */
if (work<>done) {
redirect($_SERVER["PHP_SELF"]."?1";
} else {
exit;
}
}
function redirect($url,$seconds=0) {
$html="<html><head><meta http-equiv='refresh' content='$seconds; URL=$url'></head></html>";
echo $html;
exit();
}

automatic number increase in php based web form [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
So I have created an html form which then posts the results to a php file that overlays them on a PDF and then emails that PDF to myself and the email that was put in the form. All I want to do now is find a simple way to make it so that the PDF includes a sequential number.
For example: When the form is filled out for the first time the number 0001 is input automatically into the PDF and 0002 for the second time and so on.
Is there an easy PHP function to accomplish this?
Essentially I am creating an online invoicing form so when I do service calls I can create an invoice on the spot from a web browser which is then emailed to my office and the client.
Any help would be greatly appreciated.
For an incrementing number, you could keep a number in a database and then extract it, add 1 to it, use it, and then put it back in the DB for next time, but this seems complicated. Somebody in the comments mentioned using the timestamp, which would be done like so:
$invoicenumber = time(); //This number will always be unique
The time function works like so (copied from w3schools):
The time() function returns the current time in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
Since actual seconds can only go up (increment), this number will never be the same twice.
I hope this is helpful.
-Edit
You can also display this date/time in a readable format like so:
$time = time();
echo date("Y-m-d H:i:s",$time);
-Edit 2
If you want an incrementing number, you basically need a very simple database to save it, which might be as simple as a table called invoices, with a column called invoicenumber, which stores your invoice number in it. You could / probably should use this to store other invoice information in it too, so you'd have each invoice number saved (which means we want to only get the highest one)
Then your code would look like this, for each time you want to use it:
Firstly you'd have a database information file (settings.php or something similar) with your database definitions in it, which might look like this:
define('DB_HOST', 'localhost');
define('DB_USER', 'db_username');
define('DB_PASS', 'db_password');
define('DB_NAME', 'database_name');
Your code would look like this:
//Establish a mysql connection
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
//Set up a query to get the highest number
$query = "SELECT invoicenumber FROM invoices ORDER BY invoicenumber DESC LIMIT 1";
//Get the result
$result = $mysqli->query($query);
$row = $result->fetch_assoc();
//If we have a record
if($row){
//New invoice number
$invoicenumber = $row['invoicenumber']++;
//Else (database is empty, so start at the beginning)
}else{
$invoicenumber = 1;
}
//Now we have our invoice number, so do whatever you want with it
/**
* Code here to use the number
* */
//Now we wanna add the new invoice to the database, so
/**
* Add any other info to this statement if you want.
* If any of it is user submitted data, be sure to use prepared statements
* (just look at php.net's documentation on prepared statements)
* w3schools also has some nice tutorials on how to safely insert stuff
* in to a database, so check it all out :)
* */
$query = "INSERT INTO invoices(invoicenumber) VALUES($invoicenumber)";
//Execute the query
if($mysqli->query($query)){
//Show success
echo "Invoice $invoicenumber has been added to the database.";
}else{
//Show error
echo "Unfortunately we could not add invoice $invoicenumber to the database.";
}
//Now we can clear up our resources
$stmt->free_result(); $stmt->close(); $mysqli->close();
Please note: this is a very basic example. Yours will have additions and enhanced security if you are using user submitted data, so please do your homework and make sure that you fully understand each line of this code before you proceed to use it.
I do exactly the same with patient accession numbers on patient reports.
include('/home/user/php/class.pdf2text.php');
$p2t = new PDF2Text();
$p2t->setFilename($pdf);
$p2t->decodePDF();
$data = $p2t->output();
$len = strlen($data);
$pos = strpos($data,$accession);
if (pos){
$in .= "$accession,";
$checked++;
}
else{
$missingPDF += 1;echo "\n<p> <span class='bold red'>INCORRECT ACCESSION NUMBER c=$row[0] p=$row[1]</span>\n";
}
if ($checked > 0){
$in = substr($in,0,-1) . ')';
$sql = "UPDATE `Patient` SET `PDF`=1 WHERE $in";
}
pdf2text.php
class PDF2Text {
// Some settings
var $multibyte = 4; // Use setUnicode(TRUE|FALSE)
var $convertquotes = ENT_QUOTES; // ENT_COMPAT (double-quotes), ENT_QUOTES (Both), ENT_NOQUOTES (None)
var $showprogress = true; // TRUE if you have problems with time-out
// Variables
var $filename = '';
var $decodedtext = '';
function setFilename($filename) {
// Reset
$this->decodedtext = '';
$this->filename = $filename;
}
function output($echo = false) {
if($echo) echo $this->decodedtext;
else return $this->decodedtext;
}
function setUnicode($input) {
// 4 for unicode. But 2 should work in most cases just fine
if($input == true) $this->multibyte = 4;
else $this->multibyte = 2;
}
function decodePDF() {
// Read the data from pdf file
$infile = #file_get_contents($this->filename, FILE_BINARY);
if (empty($infile))
return "";
// Get all text data.
$transformations = array();
$texts = array();
// Get the list of all objects.
preg_match_all("#obj[\n|\r](.*)endobj[\n|\r]#ismU", $infile . "endobj\r", $objects);
$objects = #$objects[1];
// Select objects with streams.
for ($i = 0; $i < count($objects); $i++) {
$currentObject = $objects[$i];
// Prevent time-out
#set_time_limit ();
if($this->showprogress) {
// echo ". ";
flush(); ob_flush();
}
// Check if an object includes data stream.
if (preg_match("#stream[\n|\r](.*)endstream[\n|\r]#ismU", $currentObject . "endstream\r", $stream )) {
$stream = ltrim($stream[1]);
// Check object parameters and look for text data.
$options = $this->getObjectOptions($currentObject);
if (!(empty($options["Length1"]) && empty($options["Type"]) && empty($options["Subtype"])) )
// if ( $options["Image"] && $options["Subtype"] )
// if (!(empty($options["Length1"]) && empty($options["Subtype"])) )
continue;
// Hack, length doesnt always seem to be correct
unset($options["Length"]);
// So, we have text data. Decode it.
$data = $this->getDecodedStream($stream, $options);
if (strlen($data)) {
if (preg_match_all("#BT[\n|\r](.*)ET[\n|\r]#ismU", $data . "ET\r", $textContainers)) {
$textContainers = #$textContainers[1];
$this->getDirtyTexts($texts, $textContainers);
} else
$this->getCharTransformations($transformations, $data);
}
}
}
// Analyze text blocks taking into account character transformations and return results.
$this->decodedtext = $this->getTextUsingTransformations($texts, $transformations);
}
function decodeAsciiHex($input) {
$output = "";
$isOdd = true;
$isComment = false;
for($i = 0, $codeHigh = -1; $i < strlen($input) && $input[$i] != '>'; $i++) {
$c = $input[$i];
if($isComment) {
if ($c == '\r' || $c == '\n')
$isComment = false;
continue;
}
switch($c) {
case '\0': case '\t': case '\r': case '\f': case '\n': case ' ': break;
case '%':
$isComment = true;
break;
default:
$code = hexdec($c);
if($code === 0 && $c != '0')
return "";
if($isOdd)
$codeHigh = $code;
else
$output .= chr($codeHigh * 16 + $code);
$isOdd = !$isOdd;
break;
}
}
if($input[$i] != '>')
return "";
if($isOdd)
$output .= chr($codeHigh * 16);
return $output;
}
function decodeAscii85($input) {
$output = "";
$isComment = false;
$ords = array();
for($i = 0, $state = 0; $i < strlen($input) && $input[$i] != '~'; $i++) {
$c = $input[$i];
if($isComment) {
if ($c == '\r' || $c == '\n')
$isComment = false;
continue;
}
if ($c == '\0' || $c == '\t' || $c == '\r' || $c == '\f' || $c == '\n' || $c == ' ')
continue;
if ($c == '%') {
$isComment = true;
continue;
}
if ($c == 'z' && $state === 0) {
$output .= str_repeat(chr(0), 4);
continue;
}
if ($c < '!' || $c > 'u')
return "";
$code = ord($input[$i]) & 0xff;
$ords[$state++] = $code - ord('!');
if ($state == 5) {
$state = 0;
for ($sum = 0, $j = 0; $j < 5; $j++)
$sum = $sum * 85 + $ords[$j];
for ($j = 3; $j >= 0; $j--)
$output .= chr($sum >> ($j * 8));
}
}
if ($state === 1)
return "";
elseif ($state > 1) {
for ($i = 0, $sum = 0; $i < $state; $i++)
$sum += ($ords[$i] + ($i == $state - 1)) * pow(85, 4 - $i);
for ($i = 0; $i < $state - 1; $i++) {
try {
if(false == ($o = chr($sum >> ((3 - $i) * 8)))) {
throw new Exception('Error');
}
$output .= $o;
} catch (Exception $e) { /*Dont do anything*/ }
}
}
return $output;
}
function decodeFlate($data) {
return #gzuncompress($data);
}
function getObjectOptions($object) {
$options = array();
if (preg_match("#<<(.*)>>#ismU", $object, $options)) {
$options = explode("/", $options[1]);
#array_shift($options);
$o = array();
for ($j = 0; $j < #count($options); $j++) {
$options[$j] = preg_replace("#\s+#", " ", trim($options[$j]));
if (strpos($options[$j], " ") !== false) {
$parts = explode(" ", $options[$j]);
$o[$parts[0]] = $parts[1];
} else
$o[$options[$j]] = true;
}
$options = $o;
unset($o);
}
return $options;
}
function getDecodedStream($stream, $options) {
$data = "";
if (empty($options["Filter"]))
$data = $stream;
else {
$length = !empty($options["Length"]) ? $options["Length"] : strlen($stream);
$_stream = substr($stream, 0, $length);
foreach ($options as $key => $value) {
if ($key == "ASCIIHexDecode")
$_stream = $this->decodeAsciiHex($_stream);
elseif ($key == "ASCII85Decode")
$_stream = $this->decodeAscii85($_stream);
elseif ($key == "FlateDecode")
$_stream = $this->decodeFlate($_stream);
elseif ($key == "Crypt") { // TO DO
}
}
$data = $_stream;
}
return $data;
}
function getDirtyTexts(&$texts, $textContainers) {
for ($j = 0; $j < count($textContainers); $j++) {
if (preg_match_all("#\[(.*)\]\s*TJ[\n|\r]#ismU", $textContainers[$j], $parts))
$texts = array_merge($texts, array(#implode('', $parts[1])));
elseif (preg_match_all("#T[d|w|m|f]\s*(\(.*\))\s*Tj[\n|\r]#ismU", $textContainers[$j], $parts))
$texts = array_merge($texts, array(#implode('', $parts[1])));
elseif (preg_match_all("#T[d|w|m|f]\s*(\[.*\])\s*Tj[\n|\r]#ismU", $textContainers[$j], $parts))
$texts = array_merge($texts, array(#implode('', $parts[1])));
}
}
function getCharTransformations(&$transformations, $stream) {
preg_match_all("#([0-9]+)\s+beginbfchar(.*)endbfchar#ismU", $stream, $chars, PREG_SET_ORDER);
preg_match_all("#([0-9]+)\s+beginbfrange(.*)endbfrange#ismU", $stream, $ranges, PREG_SET_ORDER);
for ($j = 0; $j < count($chars); $j++) {
$count = $chars[$j][1];
$current = explode("\n", trim($chars[$j][2]));
for ($k = 0; $k < $count && $k < count($current); $k++) {
if (preg_match("#<([0-9a-f]{2,4})>\s+<([0-9a-f]{4,512})>#is", trim($current[$k]), $map))
$transformations[str_pad($map[1], 4, "0")] = $map[2];
}
}
for ($j = 0; $j < count($ranges); $j++) {
$count = $ranges[$j][1];
$current = explode("\n", trim($ranges[$j][2]));
for ($k = 0; $k < $count && $k < count($current); $k++) {
if (preg_match("#<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+<([0-9a-f]{4})>#is", trim($current[$k]), $map)) {
$from = hexdec($map[1]);
$to = hexdec($map[2]);
$_from = hexdec($map[3]);
for ($m = $from, $n = 0; $m <= $to; $m++, $n++)
$transformations[sprintf("%04X", $m)] = sprintf("%04X", $_from + $n);
} elseif (preg_match("#<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+\[(.*)\]#ismU", trim($current[$k]), $map)) {
$from = hexdec($map[1]);
$to = hexdec($map[2]);
$parts = preg_split("#\s+#", trim($map[3]));
for ($m = $from, $n = 0; $m <= $to && $n < count($parts); $m++, $n++)
$transformations[sprintf("%04X", $m)] = sprintf("%04X", hexdec($parts[$n]));
}
}
}
}
function getTextUsingTransformations($texts, $transformations) {
$document = "";
for ($i = 0; $i < count($texts); $i++) {
$isHex = false;
$isPlain = false;
$hex = "";
$plain = "";
for ($j = 0; $j < strlen($texts[$i]); $j++) {
$c = $texts[$i][$j];
switch($c) {
case "<":
$hex = "";
$isHex = true;
$isPlain = false;
break;
case ">":
$hexs = str_split($hex, $this->multibyte); // 2 or 4 (UTF8 or ISO)
for ($k = 0; $k < count($hexs); $k++) {
$chex = str_pad($hexs[$k], 4, "0"); // Add tailing zero
if (isset($transformations[$chex]))
$chex = $transformations[$chex];
$document .= html_entity_decode("&#x".$chex.";");
}
$isHex = false;
break;
case "(":
$plain = "";
$isPlain = true;
$isHex = false;
break;
case ")":
$document .= $plain;
$isPlain = false;
break;
case "\\":
$c2 = $texts[$i][$j + 1];
if (in_array($c2, array("\\", "(", ")"))) $plain .= $c2;
elseif ($c2 == "n") $plain .= '\n';
elseif ($c2 == "r") $plain .= '\r';
elseif ($c2 == "t") $plain .= '\t';
elseif ($c2 == "b") $plain .= '\b';
elseif ($c2 == "f") $plain .= '\f';
elseif ($c2 >= '0' && $c2 <= '9') {
$oct = preg_replace("#[^0-9]#", "", substr($texts[$i], $j + 1, 3));
$j += strlen($oct) - 1;
$plain .= html_entity_decode("&#".octdec($oct).";", $this->convertquotes);
}
$j++;
break;
default:
if ($isHex)
$hex .= $c;
elseif ($isPlain)
$plain .= $c;
break;
}
}
$document .= "\n";
}
return $document;
}
}

Allowing multiple connection for the clients

I am having problem i could not connect all my client in my socket.it only connect one client at a time when a new connection made.how can i make all my clients send data together or connect them all.I hope someone can help me on this.
Thank you in advance.
// Set time limit to indefinite execution
set_time_limit (0);
// Set the ip and port we will listen on
$address = '192.168.0.11';//server ip demo only.
$port = 123;
$max_clients = 10;
// Array that will hold client information
$client = array();
// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port) or die('Could not bind to address');
// Start listening for connections
socket_listen($sock);
// Loop continuously
while (true) {
$file = fopen('txt.log','a');
// Setup clients listen socket for reading
$read[0] = $sock;
for ($i = 0; $i < $max_clients; $i++)
{
if (isset($client[$i]))
if ($client[$i]['sock'] != null)
$read[$i + 1] = $client[$i]['sock'] ;
}
// Set up a blocking call to socket_select()
$write = null;
$except = NULL;
$tv_sec = NULL;
echo "connecting";
$ready = socket_select($read,$write,$except,$tv_sec);
/* if a new connection is being made add it to the client array */
if (in_array($sock, $read)) {
for ($i = 0; $i < $max_clients; $i++)
{
if (!isset($client[$i])) {
$client[$i] = array();
socket_set_nonblock($sock);
$client[$i]['sock'] = socket_accept($sock);
$file = fopen('txt.log','a');
echo("Accepting incomming connection...\n");
break;
}
elseif ($i == $max_clients - 1)
print ("too many clients");
}
if (--$ready <= 0)
continue;
} // end if in_array
// If a client is trying to write - handle it now
for ($i = 0; $i < $max_clients; $i++) // for each client
{
if (isset($client[$i]))
if (in_array($client[$i]['sock'] , $read))
{
$input = socket_read($client[$i]['sock'] , 1024);
if ($input == null) {
// Zero length string meaning disconnected
echo("Client disconnected\n");
unset($client[$i]);
fclose($file);
}
$n = trim($input);
if ($n == 'exit') {
echo("Client requested disconnect\n");
// requested disconnect
socket_close($client[$i]['sock']);
}
elseif ($input) {
echo("Receiving data\n");
// strip white spaces and write back to user
$output = ($input);
//socket_write($client[$i]['sock'],$output);
fwrite($file,$output);
echo $output."\r\n";
}
} else {
// Close the socket
if (isset($client[$i]))
echo("Client disconnected\n");
if ($client[$i]['sock'] != null){
socket_close($client[$i]['sock']);
unset($client[$i]);
}
}
}
fclose($file);//closing file
} // end while
// Close the master sockets
echo("Shutting down\n");
socket_close($sock);
fclose($file);

Restricting access to a site using IP address

I would like to know whether there is a way of restricting the users of a site such that they can only access the inner pages of a site if they are within a certain range of IP addresses or a certain network?
The current PHP scripts I am getting cant differentiate the real IPs from the Proxies?
Thanks
i wouldn’t restrict on ip addresses. as you said, you can’t know if it’s a proxy. furthermore, ip addresses can be easily spoofed.
Have you considered using apache .htaccess files for that?
IP restriction with htaccess
You can try out a script I created that allows very advanced IP rules. I coded it years ago so I apologize in advance for the current shape of it.
Edit:
If you're looking for an "&" operator in the syntax don't bother. I forgot to add it when I coded this and looking back at this script now makes me cringe at the thought of touching it again.
<?php
##############################################################
# IP Expression Class #
# Easy IP-based Access Restrictions #
# Change Log: #
# - Added Range and limited IPv6 support #
# - Changed name from IPAR to IPEX #
# #
##############################################################
# Example Rules: #
# 69.[10-20].[^50].* #
# 69.*.[1-5 | 10-20 |^30].* #
# 60.12.2.* #
# 127.* #
# 69.1.1.1-70.1.1.1 <-- This is a range #
# #
# Usage: #
# Ipex::IsMatch($rule, $ip); #
# #
# [range] - Defines a range for a section of the IP #
# | - OR token. IP can match this range/number #
# ^ - NOT token. IP can not match this range/number #
# x-y - Defines a range from x to y #
# x - Exactly match x (x = a hex or dec number) #
# * - Match any number #
# #
#----------===============================-------------------#
# [ Written by Chris Tarquini ] #
#----------===============================-------------------#
##############################################################
define('IPR_DENY', false);
define('IPR_ALLOW', true);
define('IPR_ERR_MISMATCH',-1);
define('IPR_ERR_RANGE_MISMATCH',-2);
define('IPR_ERR_RANGE_INVALID',-3);
define('IPR_ERR_INVALID_RULE',-4);
class IPEX
{
const TOKEN_RANGE_BEGIN = '[';
const TOKEN_RANGE_END = ']';
const TOKEN_WILDCARD = '*';
const TOKEN_RANGE_SPLIT = '-';
const TOKEN_OR = '|';
const TOKEN_NOT = '^';
const DEBUG_MODE = TRUE;
private static function trace($err){if(self::DEBUG_MODE) echo "$err\r\n";}
private static function FixRule($rule,$count = 4, $split='.')
{
$rule = explode($split,$rule);
$filler = 0;
$size = sizeof($rule);
for($i = 0; $i < $count; $i++)
{
if($i > $size) { $rule[] = $filler; $size++;}
else if(empty($rule[$i])) { $filler = self::TOKEN_WILDCARD; $rule[$i] = $filler;}
}
return $rule;
}
private static function FixIP($rule,$count = 4, $split='.')
{
$rule = explode($split,$rule);
$size = sizeof($rule);
for($i = 0; $i < $count; $i++)
{
if($i > $size) { $rule[] = 0; $size++;}
else if(empty($rule[$i])) { $rule[$i] = 0;}
}
return $rule;
}
private static function GetIpType(&$ip)
{
$mode = IPID::Identify($ip,$newip);
if($mode == IPID_IPv4_Embed) { $ip = $newip; return IPID_IPv4;}
return $mode;
}
private static function FixIPRange(&$start, &$stop)
{
$count = 4; $split = '.';
if(self::GetIpType($start) == IPID_IPv6) {$count = 8; $split = ':';}
$q = 0;
while($q < 2)
{
$filler = ($q == 0) ? 0 : 255;
$arr = explode($split,($q == 0) ? $start : $stop);
$size = sizeof($arr);
for($i = 0; $i < $count; $i++)
{
if($i > $size){ $arr[] = $filler; $size++;}
else if(empty($arr[$i])){ $arr[$i] = $filler; }
}
if($q == 0) $start = implode($split, $arr);
else $stop = implode($split,$arr);
$q++;
}
}
public static function IsInRange($start, $stop, $ip)
{
//Sorry guys we only support IPv4 for this ;(
self::FixIPRange($start,$stop);
self::trace("fixed: start = $start, stop = $stop");
$start = ip2long($start); $stop = ip2long($stop);
$ip = ip2long($ip);
self::trace("start = $start, stop = $stop, ip = $ip");
return ($ip >= $start && $ip <= $stop);
}
public static function IsAllowed($rule, $ip){return self::IsMatch($rule,$ip);}
public static function IsMatch($rule,$ip)
{
$mode = self::GetIpType($ip);
self::trace("ip type: $mode");
if(strpos($rule, self::TOKEN_RANGE_SPLIT) !== false && strpos($rule,self::TOKEN_RANGE_BEGIN) === false)
{
self::trace("ip range mode");
$test = explode(self::TOKEN_RANGE_SPLIT, $rule);
self::trace("range size: ".sizeof($test));
print_r($test);
if(sizeof($test) != 2) return IPR_ERR_RANGE_INVALID;
$start = $test[0]; $end = $test[1];
if(empty($start) || empty($end)) return IPR_ERR_RANGE_INVALID;
self::trace("range start: $start, range stop: $end");
$rm1 = (self::IsHex($start)) ? $mode : self::GetIpType($start);
$rm2 = (self::IsHex($end)) ? $mode : self::GetIpType($end);
self::trace("range types: $rm1, $rm2\r\nip type: $mode");
if($rm1 != $rm2 || $rm1 != $mode) return IPR_ERR_RANGE_MISMATCH;
if($mode == IPID_IPv6) { return IPR_ERR_IPv6_NOTSUPPORTED;}
return self::IsInRange($start,$end,$ip);
}
if(self::GetIpType($rule) != $mode) return IPR_ERR_MISMATCH;
//all is good so far
$count = 4;
$split = '.'; if($mode==IPID_IPv6){$count = 8; $split=':';}
$rule = self::FixRule($rule, $count,$split);
$ip = self::FixIp($ip,$count,$split);
self::trace("ip: ".implode($split,$ip));
self::trace('rule: '.implode($split,$rule));
for($i = 0; $i < $count; $i++)
{
$r = str_replace(' ', '', $rule[$i]);
$ri = false;
if($r == self::TOKEN_WILDCARD) continue;
if($mode == IPPID_IPv6 && self::IsHex($r)) { $ri = hexdec($r);}else if(is_numeric($r)) $ri = $r;
$x = $ip[$i];
if($mode == IPPID_IPv6) $x = hexdec($x);
//* Exact Match *//
self::trace("rule[$i]: $ri");
self::trace("ip[$i]: $x");
if($ri !== false && $ri != $x) return IPR_DENY;
$len = strlen($r);
for($y = 0; $y < $len; $y++)
{
self::trace("y = $y");
if(substr($r, $y,1) == self::TOKEN_RANGE_BEGIN)
{
++$y;
self::trace("found range, y = $y");
$negflag = false;
$start = false;
$stop = false;
$allows = 0;
$denys = 0;
$q = 0;
$c = substr($r,$y,1);
while($c !== false)
{
self::trace("in range, char: $c");
//* Flags *//
$break = false;
$exec = false;
$toggle = false;
$reset = false;
if($c === self::TOKEN_RANGE_END) {$skiphex = true;$break = true; $exec = true; self::trace("found end of range");}
if($c === self::TOKEN_NOT) {if($q > 0){ $toggle = true; $exec = true;} else $negflag = !$negflag; $skiphex =false; self::trace("found TOKEN_NOT");}
if($c === self::TOKEN_OR) { $exec = true; $reset = true;$skiphex=true;self::trace("found TOKEN_OR");}
if($c === self::TOKEN_RANGE_SPLIT){ $skiphex = false;++$q; self::trace("found range split");}
//* Read Hex Tokens *//
if(!$skiphex && self::IsHexChar($c))
{
$n = self::ReadNextHexToken($r,$y);
if($mode == IPID_IPv6) $n = hexdec($n);
if($q == 0) $start = $n;
else if($q == 1) $stop = $n;
--$y; //fixes error
self::trace("parsed number: $n, y = $y");
}
if($reset) {$negflag = false; $start = false; $stop = false; $q = 0;}
if($exec)
{
self::trace("executing: start = $start, stop = $stop, x = $x");
self::trace("negflag = $negflag");
if($stop !== false && $x >= $start && $x <= $stop)
{
if($negflag) { ++$denys; $allows = 0; break;}
else ++$allows;
}
else if($stop === false && $start == $x)
{
if($negflag) { ++$denys; $allows = 0; break;}
else ++$allows;
}
self::trace("exec complete: allows = $allows, denys = $denys");
$q = 0;
}
if($toggle) $negflag = !$negflag;
if($break) break;
++$y;
$c = substr($r,$y,1);
}
if(!$allows) return IPR_DENY;
}
}
}
return IPR_ALLOW;
}
private static function ReadNextHexToken($buff, &$offset, $max = -1)
{
$str = '';
if($max == -1) { $max = strlen($buff);}
for(; $offset < $max; $offset++)
{
$c = substr($buff,$offset, 1);
if(self::IsHexChar($c))
$str .= $c;
else
return $str;
}
return $str;
}
private static function IsHex($x){ $len = strlen($x); for($i = 0; $i < $len; $i++) if(!self::IsHexChar(substr($x,$i,1))) return false; return true;}
private static function IsHexChar($x){self::trace("isHex($x);"); return (in_array(strtoupper($x),array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')));
}
}
######################
# IP Identify Class #
#####################
define('IPID_INVALID',false);
define('IPID_IPv4',2);
define('IPID_IPv6',3);
define('IPID_IPv4_Embed',6);
class IPID
{
public static function Identify($ip,&$ipconvert = false)
{
$ip = strtoupper($ip);
$ipconvert = $ip;
// Check if we are IPv4
if(strpos($ip,':') === false && strpos($ip,'.') !== false)
return IPID_IPv4;
//Is it one of those hybrids?
else if(strpos($ip,':FFFF') !== false && strpos($ip,'.') !== false)
{
$ipconvert = substr($ip,strpos($ip,':FFFF:')+6);
return IPID_IPv4_Embed;
}
// Is it IPv6?
else if(strpos($ip,':') !== false) return IPID_IPv6;
// What the...?
return IPID_INVALID;
}
}
?>
You can use it as long as you don't try and resell it and you keep the header as is.
<?php
//This function returns True if visitor IP is allowed.
//Otherwise it returns False.
function CheckAccess()
{
//allowed IP. Change it to your static IP
$allowedip = '127.0.0.1';
$ip = $_SERVER['REMOTE_ADDR'];
return ($ip == $allowedip);
}
Proxy servers should set the X-Forwarded-For HTTP header, which you could look up with $_SERVER['HTTP_X_FORWARDED_FOR']. Otherwise $_SERVER['REMOTE_ADDR'] can be used to get the IP address. As others have noted, both of these can be easily spoofed, and there is no requirement for proxies to set the X-Forwarded-For request header.
There is a ip2long() function in PHP which give you an integer to use for range checking.
To get the location of an IP address you need a lookup table which maps IP address ranges to approximate geographical locations (such lookup tables are typically not free). There are many services which offer IP address geolocation, some of which are mentioned here and here.

Categories