├── .gitignore ├── README.md └── swoole_amqp.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swoole-amqp 2 | 3 | An asynchronous PHP AMQP client base on swoole. 4 | 5 | ## Classes 6 | 7 | ### swoole_amqp 8 | 9 | Class `swoole_amqp`(`Swoole\\Amqp` if `swoole.use_namespace=On` was set in `php.ini`) represents client of amqp. 10 | 11 | Methods list of `swoole_amqp`: 12 | 13 | - `__construct` 14 | - `connect` 15 | - `createChannel` 16 | - `declareExchange` 17 | - `deleteExchange` 18 | - `bindExchange` 19 | - `unbindExchange` 20 | - `declareQueue` 21 | - `purgeQueue` 22 | - `deleteQueue` 23 | - `bindQueue` 24 | - `unbindQueue` 25 | - `qos` 26 | - `consume` 27 | - `on` 28 | - `ack` 29 | - `cancel` 30 | - `close` 31 | 32 | #### `__construct` 33 | 34 | `__construct($host, $port)` 35 | 36 | #### `connect` 37 | 38 | `connect($vhost, $username, $passwd[, $timeout])`. 39 | 40 | It create a connection to AMQP server, and it should be called before doing any operations. 41 | 42 | It is a blocking method actually. 43 | 44 | #### `createChannel` 45 | 46 | `createChannel($channel)` 47 | 48 | `$channel` is an integer that identify a channel, it should less than 65535. 49 | 50 | Channel represents a session with server base on a connection. It can be considered as logical connection. You can create many channel on a single connection. 51 | 52 | Most operation are base on channel. 53 | 54 | #### `declareExchange` 55 | 56 | `declareExchange(int $channel, string $exchange, string $type[, bool $passive, bool $durable, bool $autoDelete, bool $internal])` 57 | 58 | #### `deleteExchange` 59 | 60 | `deleteExchange(int $channel, string $exchange[, bool ifNotUsed])` 61 | 62 | #### `bindExchange` 63 | 64 | `bindExchange(int $channel, string $destination, string $source, string $routing)` 65 | 66 | #### `unbindExchange` 67 | 68 | `unbindExchange(int $channel, string $destination, string $source, string $routine)` 69 | 70 | #### `declareQueue` 71 | 72 | `declareQueue(int $channel, string $queueName[, bool passive = false, bool durable = false, bool autoDelete = false, bool exclusive = false])` 73 | 74 | #### `purgeQueue` 75 | 76 | `purgeQueue(int $channel, string $queueName)` 77 | 78 | #### `deleteQueue` 79 | 80 | `deleteQueue(int $channel, string $queueName[, bool $ifUnused = false, bool $ifEmpty = false])` 81 | 82 | #### `bindQueue` 83 | 84 | `bindQueue(int $channel, string $queueName, string $exchangeName, string $routing)` 85 | 86 | #### `unbindQueue` 87 | 88 | `unbindQueue(int $channel, string $queueName, string $exchangeName, string $routing)` 89 | 90 | #### `qos` 91 | 92 | `qos(int $channel, int $size, $count[, bool $global = false])` 93 | 94 | #### `consume` 95 | 96 | `consume(int $channel, string $queueName, string $tag, bool $noLocal, bool $ack, bool exclusive)` 97 | 98 | #### `on` 99 | 100 | `on(string $event, callable $callback)` 101 | 102 | `$event` could be `consume` / `channel_close` / `close`. 103 | 104 | `$callback` must be **String, Array or Closure** that specifies a function. 105 | 106 | For event `consume`, callback will be given an array value likes: 107 | 108 | ``` php 109 | Array 110 | ( 111 | [channel] => 1 112 | [delivery_tag] => 1 113 | [redelivered] => 0 114 | [consumer_tag] => amq.ctag-gGpIz8Cc-L5tv9GRmjehfA 115 | [exchange] => exchange_name 116 | [message] => body 117 | [routing_key] => the-routing-key 118 | ) 119 | ``` 120 | 121 | For event `channel_close`, callback will be given a integer specifies the channel. 122 | 123 | #### `ack` 124 | 125 | `ack(int $channel, int $deliveryTag[, bool $multiple = false])` 126 | 127 | #### `cancel` 128 | 129 | `cancel(int $channel, string $consumerTag)` 130 | 131 | #### `close` 132 | 133 | `close()` 134 | 135 | Closing the connection. This operation will destroy all the channel on this connection then close connection. 136 | 137 | # Example 138 | 139 | ``` php 140 | $rabbitmq = new swoole_amqp('127.0.0.1', 5672); 141 | $rabbitmq->connect('vhost', 'guest', 'guest'); 142 | $rabbitmq->createChannel(1); 143 | $rabbitmq->qos(1, 0, 10, 1); 144 | $rabbitmq->on('consume', function($msg) { 145 | print_r($msg); 146 | }); 147 | $rabbitmq->on('close', function() { 148 | echo "on close\n"; 149 | }); 150 | $rabbitmq->on('channel_close', function($channel) { 151 | echo "channel: $channel is closed\n"; 152 | }); 153 | 154 | $res = $rabbitmq->declareExchange(1, 'exchange.direct', 'direct'); 155 | $res = $rabbitmq->declareQueue(1, 'a.queue.name', 0, 1); 156 | 157 | $rabbitmq->bindQueue(1, 'a.queue.name', 'exchange.direct', 'a.queue.name'); 158 | 159 | $tag = $rabbitmq->consume(1, 'a.queue.name', '', 0, 1, 0); 160 | ``` 161 | 162 | 163 | -------------------------------------------------------------------------------- /swoole_amqp.c: -------------------------------------------------------------------------------- 1 | #include "php_swoole.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define SW_AMQP_INIT_CHANNEL_TABLE_SIZE 10 15 | 16 | #define SW_AMQP_DEFAULT_MAX_FRAME_SIZE 131072 17 | #define SW_AMQP_DEFAULT_PORT 5672 18 | 19 | #ifndef FALSE 20 | #define FALSE 0 21 | #endif 22 | 23 | #ifndef TRUE 24 | #define TRUE 1 25 | #endif 26 | 27 | extern swServerG SwooleG; 28 | 29 | struct timeval timeout_zero = {0, 0}; 30 | 31 | typedef struct _swoole_amqp_client_t swoole_amqp_client_t; 32 | 33 | struct _swoole_amqp_client_t { 34 | amqp_connection_state_t connection; 35 | 36 | HashTable *opened_channels; 37 | 38 | /* callbacks */ 39 | zval *on_consume; 40 | zval *on_close; 41 | zval *on_channel_close; 42 | 43 | zval _object; 44 | zval *object; 45 | 46 | int connected:1; 47 | }; 48 | 49 | static int add_connection_reactor(swoole_amqp_client_t *); 50 | static int remove_connection_reactor(swoole_amqp_client_t *); 51 | 52 | static int swoole_amqp_on_read(swReactor *, swEvent *); 53 | static int swoole_amqp_on_channel_close(swoole_amqp_client_t *, int); 54 | static void swoole_amqp_on_data(swoole_amqp_client_t *); 55 | 56 | static int swoole_amqp_close_connection(swoole_amqp_client_t *, int); 57 | 58 | static int swoole_amqp_handle_frame(swoole_amqp_client_t *, amqp_frame_t *, int); 59 | 60 | static int swoole_amqp_handle_reply(swoole_amqp_client_t *, amqp_rpc_reply_t *, int); 61 | 62 | static int add_connection_reactor(swoole_amqp_client_t *client) { 63 | 64 | php_swoole_check_reactor(); 65 | 66 | swReactor *reactor = SwooleG.main_reactor; 67 | 68 | swConnection *conn = swReactor_get(reactor, amqp_get_sockfd(client->connection)); 69 | conn->object = client; 70 | 71 | reactor->setHandle(reactor, PHP_SWOOLE_FD_AMQP | SW_EVENT_READ, 72 | swoole_amqp_on_read); 73 | if (reactor->add(reactor, amqp_get_sockfd(client->connection), PHP_SWOOLE_FD_AMQP) < 0) { 74 | swoole_php_fatal_error(E_WARNING, "swoole_event_add amqp connection failed."); 75 | return -1; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | static int remove_connection_reactor(swoole_amqp_client_t *client) { 82 | swReactor *reactor = SwooleG.main_reactor; 83 | if (reactor->del(reactor, amqp_get_sockfd(client->connection)) == SW_ERR) { 84 | swTrace("error when del\n"); 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | static int swoole_amqp_on_read(swReactor *reactor, swEvent *event) { 91 | swoole_amqp_on_data(event->socket->object); 92 | return SW_OK; 93 | } 94 | 95 | static int swoole_amqp_on_channel_close(swoole_amqp_client_t *client, int channel) { 96 | amqp_rpc_reply_t reply = amqp_channel_close(client->connection, channel, AMQP_REPLY_SUCCESS); 97 | swoole_amqp_handle_reply(client, &reply, channel); 98 | 99 | if (!client->on_channel_close) { 100 | goto unset_opened_channel; 101 | } 102 | 103 | zval *zchan; 104 | MAKE_STD_ZVAL(zchan); 105 | ZVAL_LONG(zchan, channel); 106 | 107 | zval **args[] = {&zchan}; 108 | zval *retval; 109 | 110 | if (sw_call_user_function_ex(EG(function_table), NULL, client->on_channel_close, &retval, 1, args, 0, NULL) != SUCCESS) { 111 | swoole_php_error(E_WARNING, "execute on channel close callback failure."); 112 | } 113 | sw_zval_ptr_dtor(&zchan); 114 | 115 | unset_opened_channel: 116 | 117 | return zend_hash_index_del(client->opened_channels, channel); 118 | } 119 | 120 | static void swoole_amqp_on_data(swoole_amqp_client_t *client) { 121 | amqp_connection_state_t conn = client->connection; 122 | 123 | amqp_envelope_t envelope; 124 | amqp_frame_t frame; 125 | 126 | while (1) { 127 | amqp_maybe_release_buffers(conn); 128 | amqp_rpc_reply_t reply = amqp_consume_message(conn, &envelope, &timeout_zero, 0); 129 | /* TIMEOUT */ 130 | if (reply.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION && reply.library_error == AMQP_STATUS_TIMEOUT) { 131 | /* do nothing than quit */ 132 | return ; 133 | } 134 | 135 | int frameStatus = 0, again = 1; 136 | if (reply.reply_type != AMQP_RESPONSE_NORMAL) { 137 | switch(reply.library_error) { 138 | case AMQP_STATUS_TIMEOUT: 139 | return ; 140 | case AMQP_STATUS_SOCKET_ERROR: 141 | case AMQP_STATUS_SOCKET_CLOSED: 142 | case AMQP_STATUS_CONNECTION_CLOSED: 143 | /* get socket error */ 144 | swoole_amqp_close_connection(client, 1); 145 | return; 146 | case AMQP_STATUS_UNEXPECTED_STATE: 147 | default: 148 | frameStatus = amqp_simple_wait_frame_noblock(conn, &frame, &timeout_zero); 149 | if (!swoole_amqp_handle_frame(client, &frame, frameStatus)) { 150 | return; 151 | } 152 | break; 153 | } 154 | } else { 155 | zval *zmessage; 156 | MAKE_STD_ZVAL(zmessage); 157 | array_init_size(zmessage, 7); 158 | 159 | add_assoc_long(zmessage, "channel", envelope.channel); 160 | add_assoc_long(zmessage, "delivery_tag", envelope.delivery_tag); 161 | add_assoc_bool(zmessage, "redelivered", envelope.redelivered); 162 | 163 | add_assoc_stringl(zmessage, "consumer_tag", envelope.consumer_tag.bytes, envelope.consumer_tag.len, 1); 164 | add_assoc_stringl(zmessage, "exchange", envelope.exchange.bytes, envelope.exchange.len, 1); 165 | add_assoc_stringl(zmessage, "message", envelope.message.body.bytes, envelope.message.body.len, 1); 166 | add_assoc_stringl(zmessage, "routing_key", envelope.routing_key.bytes, envelope.routing_key.len, 1); 167 | 168 | amqp_destroy_envelope(&envelope); 169 | 170 | zval **args[] = {&zmessage}; 171 | zval *retval; 172 | if (sw_call_user_function_ex(EG(function_table), NULL, client->on_consume, &retval, 1, args, 0, NULL) != SUCCESS) { 173 | swoole_php_fatal_error(E_WARNING, "execute on consume callback failure."); 174 | } 175 | 176 | if (retval != NULL) { 177 | sw_zval_ptr_dtor(&retval); 178 | } 179 | sw_zval_ptr_dtor(&zmessage); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * @desc Close channel and connection, and callback 186 | * 187 | */ 188 | static int swoole_amqp_close_connection(swoole_amqp_client_t *client, int passive) { 189 | int error = 0; 190 | if (!client->connected || !client->connection) { 191 | error = -1; 192 | goto out1; 193 | } 194 | 195 | /* close channels and callback */ 196 | HashTable *opened_channels = client->opened_channels; 197 | if (passive && !client->on_channel_close) { 198 | long *channel; 199 | HashPosition pos; 200 | for (zend_hash_internal_pointer_reset_ex(opened_channels, &pos); 201 | zend_hash_has_more_elements_ex(opened_channels, &pos) == SUCCESS; 202 | zend_hash_move_forward_ex(opened_channels, &pos)) { 203 | if (zend_hash_get_current_data_ex(opened_channels, (void *) &channel, &pos) == FAILURE || 204 | *channel <= 0) { 205 | continue; 206 | } 207 | 208 | if (!passive) { 209 | amqp_rpc_reply_t reply = amqp_channel_close(client->connection, *channel, AMQP_REPLY_SUCCESS); 210 | } 211 | 212 | swoole_amqp_on_channel_close(client, *channel); 213 | } 214 | } 215 | zend_hash_clean(opened_channels); 216 | 217 | if (!passive) { 218 | amqp_rpc_reply_t reply = amqp_connection_close(client->connection, AMQP_REPLY_SUCCESS); 219 | } 220 | 221 | if (client->on_close) { 222 | zval *retval; 223 | if (sw_call_user_function_ex(EG(function_table), NULL, client->on_close, &retval, 0, NULL, 0, NULL) != SUCCESS) { 224 | swoole_php_fatal_error(E_WARNING, "execute on connection close callback failure."); 225 | } 226 | } 227 | 228 | out1: 229 | if (client->connection) { 230 | remove_connection_reactor(client); 231 | 232 | int status = amqp_destroy_connection(client->connection); 233 | if (status != AMQP_STATUS_OK) { 234 | swoole_php_fatal_error(E_WARNING, "Destroy connection failure."); 235 | } 236 | } 237 | client->connection = NULL; 238 | client->connected = 0; 239 | sw_zval_ptr_dtor(&client->object); 240 | 241 | return error; 242 | } 243 | 244 | /** 245 | * 246 | * @return {int} keep on recv or not 247 | */ 248 | static int swoole_amqp_handle_frame(swoole_amqp_client_t *client, amqp_frame_t *frame, int status) { 249 | /* @return AMQP_STATUS_OK / AMQP_STATUS_INVALID_PARAMETER / AMQP_STATUS_TIMER_FAILURE / AMQP_STATUS_NO_MEMORY / AMQP_STATUS_BAD_AMQP_DATA*/ 250 | 251 | if (status == AMQP_STATUS_OK) { 252 | if (frame->frame_type == AMQP_FRAME_METHOD) { 253 | switch(frame->payload.method.id) { 254 | case AMQP_CONNECTION_CLOSE_METHOD: 255 | swoole_amqp_close_connection(client, 1); 256 | return FALSE; 257 | case AMQP_CHANNEL_CLOSE_METHOD: 258 | swoole_amqp_on_channel_close(client, frame->channel); 259 | return TRUE; 260 | /* TODO more */ 261 | default: 262 | break; 263 | } 264 | } /* else TODO */ 265 | 266 | } else { 267 | switch(status) { 268 | case AMQP_STATUS_TIMEOUT: 269 | return FALSE; 270 | /* close connection for other error */ 271 | default: 272 | swTrace("recv frame error, will close connection\n"); 273 | swoole_amqp_close_connection(client, 1); 274 | return FALSE; 275 | } 276 | } 277 | 278 | return TRUE; 279 | } 280 | 281 | static int swoole_amqp_handle_reply(swoole_amqp_client_t *client, amqp_rpc_reply_t *reply, int channel) { 282 | if (reply->reply_type == AMQP_RESPONSE_NORMAL) { 283 | return TRUE; 284 | } 285 | 286 | if (reply->reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION) { 287 | switch(reply->library_error) { 288 | case AMQP_STATUS_TIMEOUT: 289 | return FALSE; 290 | /* close connection for other error */ 291 | default: 292 | swoole_amqp_close_connection(client, 1); 293 | return FALSE; 294 | } 295 | return FALSE; 296 | } 297 | 298 | 299 | /* AMQP_RESPONSE_SERVER_EXCEPTION */ 300 | switch(reply->reply.id) { 301 | case AMQP_CONNECTION_CLOSE_METHOD: 302 | swoole_amqp_close_connection(client, 1); 303 | return FALSE; 304 | case AMQP_CHANNEL_CLOSE_METHOD: 305 | if (channel <= 0) { 306 | return FALSE; 307 | } 308 | swoole_amqp_on_channel_close(client, channel); 309 | /* TODO more */ 310 | break; 311 | default: 312 | break; 313 | } 314 | 315 | return FALSE; 316 | } 317 | 318 | static PHP_METHOD(swoole_amqp, __construct); 319 | static PHP_METHOD(swoole_amqp, __destruct); 320 | static PHP_METHOD(swoole_amqp, connect); 321 | static PHP_METHOD(swoole_amqp, createChannel); 322 | static PHP_METHOD(swoole_amqp, consume); 323 | static PHP_METHOD(swoole_amqp, cancel); 324 | static PHP_METHOD(swoole_amqp, on); 325 | static PHP_METHOD(swoole_amqp, close); 326 | static PHP_METHOD(swoole_amqp, qos); 327 | 328 | static PHP_METHOD(swoole_amqp, declareExchange); 329 | static PHP_METHOD(swoole_amqp, deleteExchange); 330 | static PHP_METHOD(swoole_amqp, bindExchange); 331 | static PHP_METHOD(swoole_amqp, unbindExchange); 332 | 333 | static PHP_METHOD(swoole_amqp, declareQueue); 334 | static PHP_METHOD(swoole_amqp, deleteQueue); 335 | static PHP_METHOD(swoole_amqp, bindQueue); 336 | static PHP_METHOD(swoole_amqp, unbindQueue); 337 | static PHP_METHOD(swoole_amqp, purgeQueue); 338 | 339 | static PHP_METHOD(swoole_amqp, ack); 340 | 341 | static zend_class_entry swoole_amqp_ce; 342 | zend_class_entry* swoole_amqp_class_entry_ptr; 343 | 344 | static const zend_function_entry swoole_amqp_methods[] = { 345 | PHP_ME(swoole_amqp, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 346 | PHP_ME(swoole_amqp, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) 347 | PHP_ME(swoole_amqp, connect, NULL, ZEND_ACC_PUBLIC) 348 | PHP_ME(swoole_amqp, createChannel, NULL, ZEND_ACC_PUBLIC) 349 | PHP_ME(swoole_amqp, qos, NULL, ZEND_ACC_PUBLIC) 350 | PHP_ME(swoole_amqp, ack, NULL, ZEND_ACC_PUBLIC) 351 | PHP_ME(swoole_amqp, consume, NULL, ZEND_ACC_PUBLIC) 352 | PHP_ME(swoole_amqp, cancel, NULL, ZEND_ACC_PUBLIC) 353 | ZEND_MALIAS(swoole_amqp, unconsume, cancel, NULL, ZEND_ACC_PUBLIC) 354 | PHP_ME(swoole_amqp, on, NULL, ZEND_ACC_PUBLIC) 355 | PHP_ME(swoole_amqp, close, NULL, ZEND_ACC_PUBLIC) 356 | 357 | PHP_ME(swoole_amqp, declareExchange, NULL, ZEND_ACC_PUBLIC) 358 | PHP_ME(swoole_amqp, deleteExchange, NULL, ZEND_ACC_PUBLIC) 359 | PHP_ME(swoole_amqp, bindExchange, NULL, ZEND_ACC_PUBLIC) 360 | PHP_ME(swoole_amqp, unbindExchange, NULL, ZEND_ACC_PUBLIC) 361 | PHP_ME(swoole_amqp, declareQueue, NULL, ZEND_ACC_PUBLIC) 362 | PHP_ME(swoole_amqp, deleteQueue, NULL, ZEND_ACC_PUBLIC) 363 | PHP_ME(swoole_amqp, bindQueue, NULL, ZEND_ACC_PUBLIC) 364 | PHP_ME(swoole_amqp, unbindQueue, NULL, ZEND_ACC_PUBLIC) 365 | PHP_ME(swoole_amqp, purgeQueue, NULL, ZEND_ACC_PUBLIC) 366 | PHP_FE_END 367 | }; 368 | 369 | void swoole_amqp_init(int module_number TSRMLS_DC) { 370 | SWOOLE_INIT_CLASS_ENTRY(swoole_amqp_ce, "swoole_amqp", "Swoole\\Amqp", swoole_amqp_methods); 371 | swoole_amqp_class_entry_ptr = zend_register_internal_class(&swoole_amqp_ce); 372 | 373 | zend_declare_property_string(swoole_amqp_class_entry_ptr, "ip", sizeof("ip") - 1, "127.0.0.1", ZEND_ACC_PROTECTED TSRMLS_CC); 374 | zend_declare_property_long(swoole_amqp_class_entry_ptr, "port", sizeof("port") - 1, SW_AMQP_DEFAULT_PORT, ZEND_ACC_PROTECTED TSRMLS_CC); 375 | zend_declare_property_string(swoole_amqp_class_entry_ptr, "vhost", sizeof("vhost") - 1, "/", ZEND_ACC_PROTECTED TSRMLS_CC); 376 | zend_declare_property_string(swoole_amqp_class_entry_ptr, "username", sizeof("username") - 1, "", ZEND_ACC_PROTECTED TSRMLS_CC); 377 | zend_declare_property_string(swoole_amqp_class_entry_ptr, "password", sizeof("password") - 1, "", ZEND_ACC_PROTECTED TSRMLS_CC); 378 | } 379 | 380 | static PHP_METHOD(swoole_amqp, __construct) { 381 | char *ip; 382 | zend_size_t ip_len; 383 | long port; 384 | zval *object = getThis(); 385 | 386 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &ip, &ip_len, &port) == FAILURE) { 387 | RETURN_FALSE; 388 | } 389 | 390 | if (port <= 0 || ip_len <= 0) { 391 | RETURN_FALSE; 392 | } 393 | 394 | zend_update_property_stringl(swoole_amqp_class_entry_ptr, object, "ip", sizeof("ip") - 1, ip, ip_len TSRMLS_CC); 395 | 396 | zend_update_property_long(swoole_amqp_class_entry_ptr, object, "port", sizeof("port") - 1, port TSRMLS_CC); 397 | 398 | swoole_amqp_client_t *client = emalloc(sizeof(*client)); 399 | memset(client, 0, sizeof(*client)); 400 | ALLOC_HASHTABLE(client->opened_channels); 401 | if (zend_hash_init(client->opened_channels, SW_AMQP_INIT_CHANNEL_TABLE_SIZE, NULL, NULL, 0) == FAILURE) { 402 | FREE_HASHTABLE(client->opened_channels); 403 | RETURN_FALSE; 404 | } 405 | 406 | #if PHP_MAJOR_VERSION < 7 407 | client->object = object; 408 | #else 409 | memcpy(&client->_object, object, sizeof(zval)); 410 | client->object = &client->_object; 411 | #endif 412 | 413 | swoole_set_object(object, client); 414 | } 415 | 416 | static PHP_METHOD(swoole_amqp, __destruct) { 417 | zval *object = getThis(); 418 | swoole_amqp_client_t *client = swoole_get_object(object); 419 | 420 | if (client->on_consume) { 421 | sw_zval_ptr_dtor(&client->on_consume); 422 | } 423 | 424 | if (client->on_channel_close) { 425 | sw_zval_ptr_dtor(&client->on_channel_close); 426 | } 427 | 428 | if (client->on_close) { 429 | sw_zval_ptr_dtor(&client->on_close); 430 | } 431 | 432 | swoole_set_object(object, NULL); 433 | efree(client); 434 | } 435 | 436 | static PHP_METHOD(swoole_amqp, connect) { 437 | char *username, *password, *vhost; 438 | zend_size_t username_len, password_len, vhost_len; 439 | long itimeout = 0; 440 | zval *object = getThis(); 441 | 442 | /* vhost, username, password, timeout */ 443 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &vhost, &vhost_len, &username, &username_len, 444 | &password, &password_len, &itimeout) == FAILURE) { 445 | RETURN_FALSE; 446 | } 447 | 448 | swoole_amqp_client_t *client = swoole_get_object(object); 449 | 450 | if (client->connected) { 451 | RETURN_TRUE; 452 | } 453 | 454 | if (itimeout < 0) { 455 | itimeout = 0; 456 | } 457 | 458 | zend_update_property_stringl(swoole_amqp_class_entry_ptr, object, "vhost", sizeof("vhost") - 1, vhost, vhost_len TSRMLS_CC); 459 | zend_update_property_stringl(swoole_amqp_class_entry_ptr, object, "username", sizeof("username") - 1, username, username_len TSRMLS_CC); 460 | zend_update_property_stringl(swoole_amqp_class_entry_ptr, object, "password", sizeof("password") - 1, password, password_len TSRMLS_CC); 461 | 462 | zend_update_property_long(swoole_amqp_class_entry_ptr, object, "timeout", sizeof("timeout") - 1, itimeout TSRMLS_CC); 463 | 464 | zval *zip = zend_read_property(swoole_amqp_class_entry_ptr, object, "ip", sizeof("ip") - 1, 0 TSRMLS_CC); 465 | zval *zport = zend_read_property(swoole_amqp_class_entry_ptr, object, "port", sizeof("port") - 1, 0 TSRMLS_CC); 466 | 467 | if (client->connection) { 468 | amqp_destroy_connection(client->connection); 469 | } 470 | amqp_connection_state_t conn = client->connection = amqp_new_connection(); 471 | amqp_socket_t *socket = amqp_tcp_socket_new(conn); 472 | 473 | struct timeval timeout = {itimeout, 0}; 474 | if (amqp_socket_open_noblock(socket, Z_STRVAL_P(zip), Z_LVAL_P(zport), &timeout) != AMQP_STATUS_OK) { 475 | RETURN_FALSE; 476 | } 477 | 478 | amqp_rpc_reply_t reply = amqp_login(conn, vhost, AMQP_DEFAULT_MAX_CHANNELS, SW_AMQP_DEFAULT_MAX_FRAME_SIZE, 0, 479 | AMQP_SASL_METHOD_PLAIN, username, password); 480 | if (reply.reply_type == AMQP_RESPONSE_NORMAL) { 481 | client->connected = 1; 482 | sw_zval_add_ref(&client->object); 483 | RETURN_TRUE; 484 | } 485 | 486 | RETURN_FALSE; 487 | } 488 | 489 | static PHP_METHOD(swoole_amqp, createChannel) { 490 | long channel; 491 | zval *object = getThis(); 492 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &channel) == FAILURE) { 493 | RETURN_FALSE; 494 | } 495 | 496 | if (channel <= 0) { 497 | swoole_php_fatal_error(E_WARNING, "channel must greater than 0."); 498 | RETURN_FALSE; 499 | } 500 | 501 | swoole_amqp_client_t *client = swoole_get_object(object); 502 | if (!client->connected) { 503 | swoole_php_error(E_WARNING, "amqp is not connected to server."); 504 | RETURN_FALSE; 505 | } 506 | 507 | if (zend_hash_index_exists(client->opened_channels, channel)) { 508 | swoole_php_error(E_WARNING, "Channel %d is opened, can not open a channel twice.", channel); 509 | RETURN_FALSE; 510 | } 511 | 512 | amqp_channel_open(client->connection, channel); 513 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 514 | if (reply.reply_type != AMQP_RESPONSE_NORMAL) { 515 | swoole_php_fatal_error(E_WARNING, "Failure to open channel %d.\n"); 516 | swoole_amqp_handle_reply(client, &reply, channel); 517 | RETURN_FALSE; 518 | } 519 | 520 | zend_hash_index_update(client->opened_channels, channel, &channel, sizeof(channel), NULL); 521 | 522 | RETURN_TRUE; 523 | } 524 | 525 | static PHP_METHOD(swoole_amqp, declareExchange) { 526 | long channel; 527 | /* TODO CONSTANT to name */ 528 | char *exchange_name, *type; 529 | zend_size_t name_len, type_len; 530 | zend_bool passive = 0, durable = 0, auto_delete = 0, internal = 0; 531 | /* TODO args */ 532 | 533 | zval *object = getThis(); 534 | 535 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lss|bbbb", &channel, &exchange_name, &name_len, &type, &type_len, 536 | &passive, &durable, &auto_delete, &internal) == FAILURE) { 537 | RETURN_FALSE; 538 | } 539 | 540 | swoole_amqp_client_t *client = swoole_get_object(object); 541 | 542 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 543 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 544 | RETURN_FALSE; 545 | } 546 | 547 | amqp_table_t empty_args = {0, NULL}; 548 | amqp_exchange_declare_ok_t *ok = amqp_exchange_declare(client->connection, channel, amqp_cstring_bytes(exchange_name), amqp_cstring_bytes(type), 549 | passive, durable, auto_delete, internal, empty_args); 550 | if (!ok) { 551 | swoole_php_error(E_WARNING, "Failure to declare exchange:%s.\n", exchange_name); 552 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 553 | swoole_amqp_handle_reply(client, &reply, channel); 554 | RETURN_FALSE; 555 | } 556 | RETURN_TRUE; 557 | } 558 | 559 | static PHP_METHOD(swoole_amqp, deleteExchange) { 560 | long channel; 561 | char *exchange_name; 562 | zend_size_t exchange_name_len; 563 | /* 是否只在没有bindings时删除 */ 564 | zend_bool if_unused = 0; 565 | 566 | zval *object = getThis(); 567 | 568 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|b", &channel, &exchange_name, &exchange_name_len, &if_unused) == FAILURE) { 569 | RETURN_FALSE; 570 | } 571 | 572 | swoole_amqp_client_t *client = swoole_get_object(object); 573 | 574 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 575 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 576 | RETURN_FALSE; 577 | } 578 | 579 | amqp_table_t empty_args = {0, NULL}; 580 | amqp_exchange_delete_ok_t *ok = amqp_exchange_delete(client->connection, channel, amqp_cstring_bytes(exchange_name), if_unused); 581 | 582 | if (!ok) { 583 | swoole_php_fatal_error(E_WARNING, "Error when deleting exchange %s.\n", exchange_name); 584 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 585 | swoole_amqp_handle_reply(client, &reply, channel); 586 | RETURN_FALSE; 587 | } 588 | 589 | RETURN_TRUE; 590 | } 591 | 592 | static PHP_METHOD(swoole_amqp, bindExchange) { 593 | long channel; 594 | char *destination, *source, *routing_key; 595 | zend_size_t dest_len, source_len, key_len; 596 | 597 | zval *object = getThis(); 598 | 599 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsss", &channel, &destination, &dest_len, &source, &source_len, &routing_key, &key_len) == FAILURE) { 600 | RETURN_FALSE; 601 | } 602 | 603 | swoole_amqp_client_t *client = swoole_get_object(object); 604 | 605 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 606 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 607 | RETURN_FALSE; 608 | } 609 | 610 | amqp_table_t empty_args = {0, NULL}; 611 | amqp_exchange_bind_ok_t *ok = amqp_exchange_bind(client->connection, channel, amqp_cstring_bytes(destination), 612 | amqp_cstring_bytes(source), amqp_cstring_bytes(routing_key), empty_args); 613 | if (!ok) { 614 | swoole_php_fatal_error(E_WARNING, "Failure to bind exchange, from %s to %s by %s.\n", source, destination, routing_key); 615 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 616 | swoole_amqp_handle_reply(client, &reply, channel); 617 | RETURN_FALSE; 618 | } 619 | RETURN_TRUE; 620 | } 621 | 622 | static PHP_METHOD(swoole_amqp, unbindExchange) { 623 | long channel; 624 | char *destination, *source, *routing_key; 625 | zend_size_t dest_len, source_len, key_len; 626 | 627 | zval *object = getThis(); 628 | 629 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsss", &channel, &destination, &dest_len, &source, &source_len, &routing_key, &key_len) == FAILURE) { 630 | RETURN_FALSE; 631 | } 632 | 633 | swoole_amqp_client_t *client = swoole_get_object(object); 634 | 635 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 636 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 637 | RETURN_FALSE; 638 | } 639 | 640 | amqp_table_t empty_args = {0, NULL}; 641 | amqp_exchange_unbind_ok_t *ok = amqp_exchange_unbind(client->connection, channel, amqp_cstring_bytes(destination), 642 | amqp_cstring_bytes(source), amqp_cstring_bytes(routing_key), empty_args); 643 | if (!ok) { 644 | swoole_php_fatal_error(E_WARNING, "Failure to unbind exchange, from %s to %s by %s.\n", source, destination, routing_key); 645 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 646 | swoole_amqp_handle_reply(client, &reply, channel); 647 | RETURN_FALSE; 648 | } 649 | RETURN_TRUE; 650 | } 651 | 652 | static PHP_METHOD(swoole_amqp, declareQueue) { 653 | long channel; 654 | char *queue_name; 655 | zend_size_t name_len; 656 | zend_bool passive = 0, durable = 0, auto_delete = 0, exclusive = 0; 657 | 658 | zval *object = getThis(); 659 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|bbbb", &channel, &queue_name, &name_len, &passive, &durable, &auto_delete, &exclusive) 660 | == FAILURE) { 661 | RETURN_FALSE; 662 | } 663 | 664 | swoole_amqp_client_t *client = swoole_get_object(object); 665 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 666 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 667 | RETURN_FALSE; 668 | } 669 | 670 | amqp_table_t empty_args = {0, NULL}; 671 | amqp_queue_declare_ok_t *ok = amqp_queue_declare(client->connection, channel, amqp_cstring_bytes(queue_name), 672 | passive, durable, exclusive, auto_delete, empty_args); 673 | if (!ok) { 674 | swoole_php_fatal_error(E_WARNING, "Error when declare queue %s.\n", queue_name); 675 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 676 | swoole_amqp_handle_reply(client, &reply, channel); 677 | RETURN_FALSE; 678 | } 679 | 680 | RETURN_TRUE; 681 | } 682 | 683 | static PHP_METHOD(swoole_amqp, purgeQueue) { 684 | long channel; 685 | char *queue_name; 686 | zend_size_t name_len; 687 | 688 | zval *object = getThis(); 689 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &channel, &queue_name, &name_len) == FAILURE) { 690 | RETURN_FALSE; 691 | } 692 | 693 | swoole_amqp_client_t *client = swoole_get_object(object); 694 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 695 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 696 | RETURN_FALSE; 697 | } 698 | 699 | amqp_table_t empty_args = {0, NULL}; 700 | amqp_queue_purge_ok_t *ok = amqp_queue_purge(client->connection, channel, amqp_cstring_bytes(queue_name)); 701 | if (!ok) { 702 | swoole_php_fatal_error(E_WARNING, "Error when purging queue %s.\n", queue_name); 703 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 704 | swoole_amqp_handle_reply(client, &reply, channel); 705 | RETURN_FALSE; 706 | } 707 | 708 | RETURN_LONG(ok->message_count); 709 | } 710 | 711 | static PHP_METHOD(swoole_amqp, deleteQueue) { 712 | long channel; 713 | char *queue_name; 714 | zend_size_t queue_name_len; 715 | /* 是否只在没有consumer时 | 队列没有消息时 删除,不满足条件则会报错 */ 716 | zend_bool if_unused = 0, if_empty = 0; 717 | 718 | zval *object = getThis(); 719 | 720 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|bb", &channel, &queue_name, &queue_name_len, &if_unused, &if_empty) == FAILURE) { 721 | RETURN_FALSE; 722 | } 723 | 724 | swoole_amqp_client_t *client = swoole_get_object(object); 725 | 726 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 727 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 728 | RETURN_FALSE; 729 | } 730 | 731 | amqp_table_t empty_args = {0, NULL}; 732 | amqp_queue_delete_ok_t *ok = amqp_queue_delete(client->connection, channel, amqp_cstring_bytes(queue_name), if_unused, if_empty); 733 | 734 | if (!ok) { 735 | swoole_php_fatal_error(E_WARNING, "Error when deleting queue %s.\n", queue_name); 736 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 737 | swoole_amqp_handle_reply(client, &reply, channel); 738 | RETURN_FALSE; 739 | } 740 | 741 | RETURN_LONG(ok->message_count); 742 | } 743 | 744 | static PHP_METHOD(swoole_amqp, bindQueue) { 745 | long channel; 746 | char *queue_name, *exchange_name, *routing_key; 747 | zend_size_t queue_name_len, exchange_name_len, routing_key_len; 748 | 749 | zval *object = getThis(); 750 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsss", &channel, &queue_name, &queue_name_len, &exchange_name, &exchange_name_len, 751 | &routing_key, &routing_key_len) == FAILURE) { 752 | RETURN_FALSE; 753 | } 754 | 755 | swoole_amqp_client_t *client = swoole_get_object(object); 756 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 757 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 758 | RETURN_FALSE; 759 | } 760 | 761 | amqp_table_t empty_args = {0, NULL}; 762 | amqp_queue_bind_ok_t *ok = amqp_queue_bind(client->connection, channel, amqp_cstring_bytes(queue_name), amqp_cstring_bytes(exchange_name), 763 | amqp_cstring_bytes(routing_key), empty_args); 764 | if (!ok) { 765 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 766 | swoole_amqp_handle_reply(client, &reply, channel); 767 | RETURN_FALSE; 768 | } 769 | 770 | RETURN_TRUE; 771 | } 772 | 773 | static PHP_METHOD(swoole_amqp, unbindQueue) { 774 | long channel; 775 | char *queue_name, *exchange_name, *routing_key; 776 | zend_size_t queue_name_len, exchange_name_len, routing_key_len; 777 | 778 | zval *object = getThis(); 779 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsss", &channel, &queue_name, &queue_name_len, &exchange_name, &exchange_name_len, 780 | &routing_key, &routing_key_len) == FAILURE) { 781 | RETURN_FALSE; 782 | } 783 | 784 | swoole_amqp_client_t *client = swoole_get_object(object); 785 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 786 | swoole_php_error(E_WARNING, "Channel %d is not a available channle.\n", channel); 787 | RETURN_FALSE; 788 | } 789 | 790 | amqp_table_t empty_args = {0, NULL}; 791 | amqp_queue_unbind_ok_t *ok = amqp_queue_unbind(client->connection, channel, amqp_cstring_bytes(queue_name), amqp_cstring_bytes(exchange_name), 792 | amqp_cstring_bytes(routing_key), empty_args); 793 | if (!ok) { 794 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 795 | swoole_amqp_handle_reply(client, &reply, channel); 796 | RETURN_FALSE; 797 | } 798 | 799 | RETURN_TRUE; 800 | } 801 | 802 | /** 803 | * @param {long} channel 804 | * @param {long} size 805 | * @param {long} count 806 | * @param {bool} global. defaults to false 807 | * 808 | * @return {bool} 809 | */ 810 | static PHP_METHOD(swoole_amqp, qos) { 811 | long size, count, channel; 812 | zend_bool global = 0; 813 | zval *object = getThis(); 814 | 815 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll|b", &channel, &size, &count, &global) == FAILURE) { 816 | RETURN_FALSE; 817 | } 818 | 819 | if (channel <= 0) { 820 | RETURN_FALSE; 821 | } 822 | 823 | swoole_amqp_client_t *client = swoole_get_object(object); 824 | if (!client->connected) { 825 | swoole_php_error(E_WARNING, "amqp is not connected to server."); 826 | RETURN_FALSE; 827 | } 828 | 829 | if (size < 0) { 830 | size = 0; 831 | } 832 | if (count < 0) { 833 | count = 0; 834 | } 835 | 836 | amqp_basic_qos_ok_t *ok = amqp_basic_qos(client->connection, channel, size, count, global); 837 | 838 | RETURN_BOOL((ok != NULL)); 839 | } 840 | 841 | /** 842 | * @desc 应当在最后调用 843 | * @param {int} $channle 844 | * @param {string} $queueName 845 | * @param {string} $tag 846 | * @param {bool} $noLocal 847 | * @param {bool} $needAck 848 | * @param {bool} $exclusive 849 | * 850 | * @return {bool} whether consume success 851 | */ 852 | static PHP_METHOD(swoole_amqp, consume) { 853 | long channel; 854 | char *queue, *tag; 855 | zend_size_t queue_len, tag_len; 856 | zend_bool no_local, ack, exclusive; 857 | zval *object = getThis(); 858 | /* args */ 859 | 860 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lssbbb", &channel, &queue, &queue_len, &tag, &tag_len, 861 | &no_local, &ack, &exclusive) == FAILURE) { 862 | RETURN_FALSE; 863 | } 864 | 865 | swoole_amqp_client_t *client = swoole_get_object(object); 866 | 867 | if (queue_len <= 0) { 868 | RETURN_FALSE; 869 | } 870 | 871 | if (!client->connected) { 872 | RETURN_FALSE; 873 | } 874 | 875 | if (!client->on_consume) { 876 | swoole_php_error(E_WARNING, "Can not starts consuming before set the consuming callback."); 877 | RETURN_FALSE; 878 | } 879 | 880 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 881 | swoole_php_error(E_WARNING, "Channel %ld is not opened, can not consume on it.\n", channel); 882 | RETURN_FALSE; 883 | } 884 | 885 | amqp_bytes_t tag_bytes = tag_len > 0 ? amqp_cstring_bytes(tag) : amqp_empty_bytes; 886 | amqp_basic_consume_ok_t *ok = amqp_basic_consume(client->connection, channel, amqp_cstring_bytes(queue), tag_bytes, 887 | no_local, !ack, exclusive, amqp_empty_table); 888 | if (!ok) { 889 | swoole_php_fatal_error(E_WARNING, "Failure to consume queue:%s.\n", queue); 890 | amqp_rpc_reply_t reply = amqp_get_rpc_reply(client->connection); 891 | swoole_amqp_handle_reply(client, &reply, channel); 892 | RETURN_FALSE; 893 | } 894 | 895 | add_connection_reactor(client); 896 | /* MUST check message at once for checking recv buffer. */ 897 | swoole_amqp_on_data(client); 898 | 899 | RETURN_STRINGL(ok->consumer_tag.bytes, ok->consumer_tag.len, 1); 900 | } 901 | 902 | static PHP_METHOD(swoole_amqp, on) { 903 | char *event; 904 | zend_size_t event_len; 905 | zval *cb, *object = getThis(); 906 | 907 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz!", &event, &event_len, &cb) == FAILURE) { 908 | RETURN_FALSE; 909 | } 910 | 911 | if (event_len <= 0) { 912 | RETURN_FALSE; 913 | } 914 | 915 | #if PHP_MAJOR_VERSION >= 7 916 | zval *_tmp = emalloc(sizeof(zval)); 917 | memcpy(_tmp, cb, sizeof(zval)); 918 | cb = _tmp; 919 | #endif 920 | 921 | swoole_amqp_client_t *client = swoole_get_object(object); 922 | 923 | if (strncasecmp("consume", event, event_len) == 0) { 924 | client->on_consume = cb; 925 | } else if (strncasecmp("close", event, event_len) == 0) { 926 | client->on_close = cb; 927 | } else if (strncasecmp("channel_close", event, event_len) == 0) { 928 | client->on_channel_close = cb; 929 | } else { 930 | RETURN_FALSE; 931 | } 932 | sw_zval_add_ref(&cb); 933 | 934 | RETURN_TRUE; 935 | } 936 | 937 | static PHP_METHOD(swoole_amqp, ack) { 938 | long channel, delivery_tag; 939 | zend_bool multiple = 0; 940 | zval *object = getThis(); 941 | 942 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|b", &channel, &delivery_tag, &multiple) == FAILURE) { 943 | RETURN_FALSE; 944 | } 945 | 946 | /* TODO check channel*/ 947 | if (channel <= 0 || delivery_tag <= 0) { 948 | RETURN_FALSE; 949 | } 950 | 951 | swoole_amqp_client_t *client = swoole_get_object(object); 952 | if (!client->connected) { 953 | swoole_php_error(E_WARNING, "Can not ack message, client is not connected to server."); 954 | RETURN_FALSE; 955 | } 956 | 957 | RETURN_LONG(amqp_basic_ack(client->connection, channel, delivery_tag, multiple)); 958 | } 959 | 960 | static PHP_METHOD(swoole_amqp, cancel) { 961 | char *consumer_tag; 962 | zend_size_t tag_len; 963 | zval *object = getThis(); 964 | long channel; 965 | 966 | swoole_amqp_client_t *client = swoole_get_object(object); 967 | 968 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &channel, &consumer_tag, &tag_len) == FAILURE) { 969 | RETURN_FALSE; 970 | } 971 | 972 | if (channel <= 0 || !zend_hash_index_exists(client->opened_channels, channel)) { 973 | swoole_php_error(E_WARNING, "Channel %ld is not a available channle.\n", channel); 974 | RETURN_FALSE; 975 | } 976 | 977 | if (tag_len <= 0) { 978 | swoole_php_error(E_WARNING, "\"%s\" is not a valid consumer tag.\n", consumer_tag); 979 | RETURN_FALSE; 980 | } 981 | 982 | if (!client->connected) { 983 | swoole_php_error(E_WARNING, "Client is not connected to server.\n"); 984 | RETURN_FALSE; 985 | } 986 | 987 | amqp_basic_cancel_ok_t *ok = amqp_basic_cancel(client->connection, channel, amqp_cstring_bytes(consumer_tag)); 988 | RETURN_BOOL((ok != NULL)); 989 | } 990 | 991 | static PHP_METHOD(swoole_amqp, close) { 992 | zval *object = getThis(); 993 | 994 | swoole_amqp_client_t *client = swoole_get_object(object); 995 | 996 | if (!client->connected) { 997 | swoole_php_error(E_WARNING, "Client is not connected to server, can not close it."); 998 | RETURN_FALSE; 999 | } 1000 | 1001 | swoole_amqp_close_connection(client, 0); 1002 | } 1003 | --------------------------------------------------------------------------------