@@ -32,6 +32,7 @@ class Connection {
3232
3333 private $ socket , $ bson ;
3434 private $ packet = 1 ;
35+ public $ compression = null ;
3536 public $ server = null ;
3637 public $ lastUsed = null ;
3738
@@ -98,6 +99,10 @@ public function establish($options= [], $auth= null) {
9899 'driver ' => ['name ' => 'XP MongoDB Connectivity ' , 'version ' => '3.0.0 ' ],
99100 'os ' => ['name ' => php_uname ('s ' ), 'type ' => PHP_OS , 'architecture ' => php_uname ('m ' ), 'version ' => php_uname ('r ' )]
100101 ],
102+ 'compression ' => ($ param = ($ options ['params ' ]['compressors ' ] ?? null ))
103+ ? explode (', ' , $ param )
104+ : []
105+ ,
101106 ];
102107
103108 // If the optional field saslSupportedMechs is specified, the command also returns
@@ -113,6 +118,7 @@ public function establish($options= [], $auth= null) {
113118
114119 try {
115120 $ this ->server = $ this ->hello ($ params );
121+ $ this ->compression = Compression::negotiate ($ this ->server ['compression ' ] ?? [], $ options ['params ' ] ?? []);
116122 } catch (ProtocolException $ e ) {
117123 throw new ConnectException ('Server handshake failed @ ' .$ this ->address (), $ e );
118124 }
@@ -230,14 +236,30 @@ public function send($operation, $header, $sections, $readPreference= null) {
230236
231237 $ this ->packet > 2147483647 ? $ this ->packet = 1 : $ this ->packet ++;
232238 $ body = $ header .$ this ->bson ->sections ($ sections );
233- $ payload = pack ('VVVV ' , strlen ($ body ) + 16 , $ this ->packet , 0 , $ operation ).$ body ;
239+ $ length = strlen ($ body );
240+
241+ if ($ this ->compression && $ compressor = $ this ->compression ->for ($ sections , $ length )) {
242+ $ compressed = $ compressor ->compress ($ body );
243+ $ this ->socket ->write (pack (
244+ 'VVVVVVCa* ' ,
245+ strlen ($ compressed ) + 25 ,
246+ $ this ->packet ,
247+ 0 ,
248+ self ::OP_COMPRESSED ,
249+ $ operation ,
250+ $ length ,
251+ $ compressor ->id ,
252+ $ compressed
253+ ));
254+ } else {
255+ $ this ->socket ->write (pack ('VVVV ' , $ length + 16 , $ this ->packet , 0 , $ operation ).$ body );
256+ }
234257
235- $ this ->socket ->write ($ payload );
236258 $ meta = unpack ('VmessageLength/VrequestID/VresponseTo/VopCode ' , $ this ->read0 (16 ));
237259 $ response = $ this ->read0 ($ meta ['messageLength ' ] - 16 );
238260 $ this ->lastUsed = time ();
239261
240- switch ($ meta ['opCode ' ]) {
262+ opcode: switch ($ meta ['opCode ' ]) {
241263 case self ::OP_MSG :
242264 $ flags = unpack ('V ' , $ response , 4 )[1 ];
243265 if ("\x00" === $ response [4 ]) {
@@ -258,6 +280,17 @@ public function send($operation, $header, $sections, $readPreference= null) {
258280
259281 return $ reply ;
260282
283+ case self ::OP_COMPRESSED :
284+ $ compressed = unpack ('VoriginalOpcode/VuncompressedSize/CcompressorId ' , $ response );
285+
286+ if ($ this ->compression && $ compressor = $ this ->compression ->select ($ compressed ['compressorId ' ] ?? null )) {
287+ $ response = $ compressor ->decompress (substr ($ response , 9 ));
288+ $ meta ['opCode ' ]= $ compressed ['originalOpcode ' ];
289+ goto opcode;
290+ }
291+
292+ throw new ProtocolException ('Unsupported compressorId ' .$ compressed ['compressorId ' ]);
293+
261294 default :
262295 return ['opCode ' => $ meta ['opCode ' ]];
263296 }
0 commit comments