push  latest
PHP shell
Protocol.php
1 <?php
2 
3 namespace fpoirotte\push;
4 
5 abstract class Protocol
6 {
7  protected $socket;
8  protected $opcodes;
9 
10  public function __construct($peer)
11  {
12  // Create a mapping of all the opcodes defined by the remote peer
13  // to each operation's name.
14  // This is used later on to call the appropriate handler for an opcode.
15  $opcodes = array();
16  $reflect = new \ReflectionClass($peer);
17  foreach ($reflect->getConstants() as $name => $value) {
18  if (!strncmp($name, 'OP_', 3)) {
19  $opcodes[ord($value)] = (string) substr($name, 3);
20  }
21  }
22  $this->opcodes = $opcodes;
23  }
24 
25  protected function send($op, $data = '')
26  {
27  // Prevent overflows.
28  if (strlen($data) > 65535) {
29  throw new \RuntimeException();
30  }
31 
32  // Encode the operation & payload,
33  // and ensure the complete message is sent to the remote peer.
34  $data = $op . pack('n', strlen($data)) . $data;
35  $len = strlen($data);
36  $written = 0;
37  while ($written < $len) {
38  $wrote = @fwrite($this->socket, substr($data, $written));
39  if ($wrote === false) {
40  throw \RuntimeException();
41  }
42 
43  $written += $wrote;
44  }
45  }
46 
47  protected function receive()
48  {
49  // Read the opcode and payload size.
50  $res = '';
51  while (($len = strlen($res)) != 3) {
52  $read = @fread($this->socket, 3 - $len);
53  if (feof($this->socket)) {
54  throw new \RuntimeException();
55  }
56  if ($read !== false) {
57  $res .= $read;
58  }
59  }
60  list($op, $dataLen) = array_values(unpack('cop/ndata', $res));
61 
62  // Read the actual payload.
63  $data = '';
64  while (($len = strlen($data)) != $dataLen) {
65  $read = @fread($this->socket, $dataLen - $len);
66  if (feof($this->socket)) {
67  throw new \RuntimeException();
68  }
69  if ($read !== false) {
70  $data .= $read;
71  }
72  }
73 
74  return array($op, $data);
75  }
76 
77  public function runOnce()
78  {
79  // Read an operation that needs to be processed.
80  list($op, $data) = $this->receive();
81 
82  // Make sure the opcode is recognized.
83  if (!isset($this->opcodes[$op])) {
84  throw new \RuntimeException();
85  }
86 
87  // Call the handler for that operation (if one has been defined).
88  $handler = 'handle' . $this->opcodes[$op];
89  if (method_exists($this, $handler)) {
90  return call_user_func(array($this, $handler), $data);
91  }
92  }
93 }