This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-php.git


The following commit(s) were added to refs/heads/master by this push:
     new b3e7290  Add amqplib plugin for producer. (#64)
b3e7290 is described below

commit b3e7290cb89e6ec8416d7e88b7263cf0c0756a00
Author: jmjoy <[email protected]>
AuthorDate: Sat Apr 15 10:42:26 2023 +0800

    Add amqplib plugin for producer. (#64)
---
 README.md                                          |   1 +
 Vagrantfile                                        |   1 +
 docker-compose.yml                                 |   8 +
 .../service-agent/php-agent/Supported-list.md      |   1 +
 src/component.rs                                   |   1 +
 src/context.rs                                     |  19 +-
 src/errors.rs                                      |   7 +
 src/execute.rs                                     |   6 +-
 src/plugin/mod.rs                                  |   2 +
 src/plugin/plugin_amqplib.rs                       | 177 ++++++++++++
 src/plugin/plugin_curl.rs                          |  15 +-
 src/tag.rs                                         |   4 +
 tests/data/expected_context.yaml                   |  64 ++++-
 tests/e2e.rs                                       |   9 +
 tests/php/composer.json                            |   3 +-
 tests/php/composer.lock                            | 310 ++++++++++++++++++++-
 tests/php/fpm/rabbitmq.php                         |  49 ++++
 17 files changed, 657 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index 0d39aae..03e0731 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ SkyWalking PHP Agent requires SkyWalking 8.4+ and PHP 7.2+
   * [ ] [php-amqp](https://github.com/php-amqp/php-amqp)
   * [ ] [php-rdkafka](https://github.com/arnaud-lb/php-rdkafka)
   * [x] [predis](https://github.com/predis/predis)
+  * [x] [php-amqplib](https://github.com/php-amqplib/php-amqplib) for Message 
Queuing Producer
 
 * Swoole Ecosystem
   * [ ] 
[Coroutine\Http\Client](https://wiki.swoole.com/#/coroutine_client/http_client)
diff --git a/Vagrantfile b/Vagrantfile
index af9fb2c..e826de0 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -23,6 +23,7 @@ Vagrant.configure("2") do |config|
     config.vm.network "forwarded_port", guest: 3306, host: 3306
     config.vm.network "forwarded_port", guest: 6379, host: 6379
     config.vm.network "forwarded_port", guest: 11211, host: 11211
+    config.vm.network "forwarded_port", guest: 5672, host: 5672
 
     config.vm.synced_folder ".", "/vagrant"
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 99fd301..825d64a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -43,3 +43,11 @@ services:
     image: memcached:1.6.17
     ports:
       - "11211:11211"
+
+  rabbitmq:
+    image: rabbitmq:3.11.13
+    ports:
+      - "5672:5672"
+    environment:
+      - RABBITMQ_DEFAULT_USER=guest
+      - RABBITMQ_DEFAULT_PASS=guest
diff --git a/docs/en/setup/service-agent/php-agent/Supported-list.md 
b/docs/en/setup/service-agent/php-agent/Supported-list.md
index 58ea590..1f99cce 100644
--- a/docs/en/setup/service-agent/php-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/php-agent/Supported-list.md
@@ -14,6 +14,7 @@ The following plugins provide the distributed tracing 
capability.
 * [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
 * [Memcached](https://www.php.net/manual/en/book.memcached.php)
 * [phpredis](https://github.com/phpredis/phpredis)
+* [php-amqplib](https://github.com/php-amqplib/php-amqplib) for Message 
Queuing Producer
 
 ## Supported PHP library
 
diff --git a/src/component.rs b/src/component.rs
index b6aaf08..6e42e71 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -24,3 +24,4 @@ pub const COMPONENT_PHP_MYSQLI_ID: i32 = 8004;
 pub const COMPONENT_PHP_PREDIS_ID: i32 = 8006;
 pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
 pub const COMPONENT_PHP_REDIS_ID: i32 = 7;
+pub const COMPONENT_AMQP_PRODUCER_ID: i32 = 144;
diff --git a/src/context.rs b/src/context.rs
index 88ab652..181efaa 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -16,7 +16,11 @@
 use anyhow::anyhow;
 use dashmap::DashMap;
 use once_cell::sync::Lazy;
-use skywalking::trace::{span::Span, trace_context::TracingContext};
+use skywalking::trace::{
+    propagation::encoder::encode_propagation, span::Span, 
trace_context::TracingContext,
+};
+
+pub const SW_HEADER: &str = "sw8";
 
 static REQUEST_CONTEXT: Lazy<DashMap<Option<i64>, RequestContext>> = 
Lazy::new(DashMap::new);
 
@@ -50,8 +54,19 @@ impl RequestContext {
         Self::try_with_global(request_id, |ctx| f(&mut ctx.tracing_context))
     }
 
+    pub fn try_get_sw_header(request_id: Option<i64>) -> crate::Result<String> 
{
+        Ok(Self::try_with_global(request_id, |req_ctx| {
+            let span_object = req_ctx.get_primary_span().span_object();
+            Ok(encode_propagation(
+                &req_ctx.tracing_context,
+                &span_object.operation_name,
+                &span_object.peer,
+            ))
+        })?)
+    }
+
     /// Primary endpoint name is used for endpoint dependency.
-    pub fn get_primary_span(&self) -> &Span {
+    fn get_primary_span(&self) -> &Span {
         &self.entry_span
     }
 }
diff --git a/src/errors.rs b/src/errors.rs
index ce87805..4de992a 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -13,6 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use anyhow::anyhow;
 use std::{result, str::Utf8Error};
 
 pub type Result<T> = result::Result<T, Error>;
@@ -37,3 +38,9 @@ impl From<url::ParseError> for Error {
         Self::Anyhow(e.into())
     }
 }
+
+impl From<String> for Error {
+    fn from(e: String) -> Self {
+        Self::Anyhow(anyhow!("{}", e))
+    }
+}
diff --git a/src/execute.rs b/src/execute.rs
index ea82d3a..4ec1801 100644
--- a/src/execute.rs
+++ b/src/execute.rs
@@ -208,12 +208,10 @@ unsafe extern "C" fn execute_ex(execute_data: *mut 
sys::zend_execute_data) {
 
     // If before hook return error, don't execute the after hook.
     if let Ok(data) = result {
+        let mut null = ZVal::from(());
         let return_value = match 
ZVal::try_from_mut_ptr((*execute_data.as_mut_ptr()).return_value) {
             Some(return_value) => return_value,
-            None => {
-                error!(err = "return value is null", "after execute ex");
-                return;
-            }
+            None => &mut null,
         };
         if let Err(err) = catch_unwind_result(AssertUnwindSafe(|| {
             after(request_id, data, execute_data, return_value)
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index e16ad07..8826c2b 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -13,6 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+mod plugin_amqplib;
 mod plugin_curl;
 mod plugin_memcached;
 mod plugin_mysqli;
@@ -35,6 +36,7 @@ static PLUGINS: Lazy<Vec<Box<DynPlugin>>> = Lazy::new(|| {
         Box::<plugin_predis::PredisPlugin>::default(),
         Box::<plugin_memcached::MemcachedPlugin>::default(),
         Box::<plugin_redis::RedisPlugin>::default(),
+        Box::<plugin_amqplib::AmqplibPlugin>::default(),
     ]
 });
 
diff --git a/src/plugin/plugin_amqplib.rs b/src/plugin/plugin_amqplib.rs
new file mode 100644
index 0000000..c894a5a
--- /dev/null
+++ b/src/plugin/plugin_amqplib.rs
@@ -0,0 +1,177 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::Plugin;
+use crate::{
+    component::COMPONENT_AMQP_PRODUCER_ID,
+    context::{RequestContext, SW_HEADER},
+    execute::{get_this_mut, validate_num_args, AfterExecuteHook, 
BeforeExecuteHook, Noop},
+    tag::{TAG_MQ_BROKER, TAG_MQ_QUEUE, TAG_MQ_TOPIC},
+};
+use anyhow::Context;
+use phper::{
+    arrays::ZArray,
+    classes::ClassEntry,
+    functions::call,
+    objects::{ZObj, ZObject},
+    values::{ExecuteData, ZVal},
+};
+use skywalking::{skywalking_proto::v3::SpanLayer, trace::span::Span};
+
+#[derive(Default, Clone)]
+pub struct AmqplibPlugin;
+
+impl Plugin for AmqplibPlugin {
+    fn class_names(&self) -> Option<&'static [&'static str]> {
+        Some(&["PhpAmqpLib\\Channel\\AMQPChannel"])
+    }
+
+    fn function_name_prefix(&self) -> Option<&'static str> {
+        None
+    }
+
+    fn hook(
+        &self, class_name: Option<&str>, function_name: &str,
+    ) -> Option<(
+        Box<crate::execute::BeforeExecuteHook>,
+        Box<crate::execute::AfterExecuteHook>,
+    )> {
+        match (class_name, function_name) {
+            (
+                Some(class_name @ "PhpAmqpLib\\Channel\\AMQPChannel"),
+                function_name @ "basic_publish",
+            ) => Some(self.hook_channel_basic_publish(class_name, 
function_name)),
+            _ => None,
+        }
+    }
+}
+
+impl AmqplibPlugin {
+    fn hook_channel_basic_publish(
+        &self, class_name: &str, function_name: &str,
+    ) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+        let class_name = class_name.to_owned();
+        let function_name = function_name.to_owned();
+        (
+            Box::new(move |request_id, execute_data| {
+                validate_num_args(execute_data, 3)?;
+
+                let this = get_this_mut(execute_data)?;
+
+                let peer = Self::get_peer(this);
+
+                let exchange = execute_data
+                    .get_parameter(1)
+                    .as_z_str()
+                    .and_then(|s| s.to_str().ok())
+                    .map(ToOwned::to_owned)
+                    .unwrap_or_else(|| "unknown".to_owned());
+
+                let routing_key = execute_data
+                    .get_parameter(2)
+                    .as_z_str()
+                    .and_then(|s| s.to_str().ok())
+                    .map(ToOwned::to_owned)
+                    .unwrap_or_else(|| "unknown".to_owned());
+
+                let span = Self::create_exit_span(
+                    request_id,
+                    &class_name,
+                    &function_name,
+                    &peer,
+                    &exchange,
+                    &routing_key,
+                )?;
+
+                Self::inject_sw_header(request_id, execute_data)?;
+
+                Ok(Box::new(span))
+            }),
+            Noop::noop(),
+        )
+    }
+
+    fn get_peer(this: &mut ZObj) -> String {
+        let Some(io) = 
this.get_property("connection").as_z_obj().and_then(|connection| 
connection.get_property("io").as_z_obj()) else {
+            return "unknown:0".to_owned();
+        };
+        let host = io
+            .get_property("host")
+            .as_z_str()
+            .and_then(|s| s.to_str().ok())
+            .unwrap_or("unknown");
+        let port = io.get_property("port").as_long().unwrap_or_default();
+        format!("{}:{}", host, port)
+    }
+
+    fn create_exit_span(
+        request_id: Option<i64>, class_name: &str, function_name: &str, peer: 
&str, exchange: &str,
+        routing_key: &str,
+    ) -> crate::Result<Span> {
+        let mut span = RequestContext::try_with_global_ctx(request_id, |ctx| {
+            Ok(ctx.create_exit_span(&format!("{}->{}", class_name, 
function_name), peer))
+        })?;
+
+        let mut span_object = span.span_object_mut();
+        span_object.set_span_layer(SpanLayer::Mq);
+        span_object.component_id = COMPONENT_AMQP_PRODUCER_ID;
+        span_object.add_tag(TAG_MQ_BROKER, peer);
+        span_object.add_tag(TAG_MQ_TOPIC, exchange);
+        span_object.add_tag(TAG_MQ_QUEUE, routing_key);
+        drop(span_object);
+
+        Ok(span)
+    }
+
+    fn inject_sw_header(
+        request_id: Option<i64>, execute_data: &mut ExecuteData,
+    ) -> crate::Result<()> {
+        const HEADER_NAME: &str = "application_headers";
+
+        let sw_header = RequestContext::try_get_sw_header(request_id)?;
+
+        let message = execute_data
+            .get_parameter(0)
+            .as_mut_z_obj()
+            .context("message isn't object")?;
+
+        let has = message
+            .call("has", [ZVal::from(HEADER_NAME)])?
+            .expect_bool()?;
+        if has {
+            let mut headers = message.call("get", [ZVal::from(HEADER_NAME)])?;
+            let headers = headers.expect_mut_z_obj()?;
+            headers.call("set", [ZVal::from(SW_HEADER), 
ZVal::from(sw_header)])?;
+        } else {
+            let headers = Self::new_sw_headers(&sw_header)?;
+            message.call("set", [ZVal::from(HEADER_NAME), 
ZVal::from(headers)])?;
+        }
+
+        Ok(())
+    }
+
+    fn new_sw_headers(sw_header: &str) -> crate::Result<ZObject> {
+        let mut arr = ZArray::new();
+        arr.insert(SW_HEADER, sw_header);
+
+        let class_name = "PhpAmqpLib\\Wire\\AMQPTable";
+        let exists = call("class_exists", [ZVal::from(class_name), 
ZVal::from(true)])?;
+        if !exists.as_bool().unwrap_or_default() {
+            return Err(format!("Class {} not exists", class_name).into());
+        }
+        let obj = 
ClassEntry::from_globals(class_name)?.new_object([ZVal::from(arr)])?;
+        Ok(obj)
+    }
+}
diff --git a/src/plugin/plugin_curl.rs b/src/plugin/plugin_curl.rs
index 9a2e218..71eb0db 100644
--- a/src/plugin/plugin_curl.rs
+++ b/src/plugin/plugin_curl.rs
@@ -16,7 +16,7 @@
 use super::Plugin;
 use crate::{
     component::COMPONENT_PHP_CURL_ID,
-    context::RequestContext,
+    context::{RequestContext, SW_HEADER},
     execute::{validate_num_args, AfterExecuteHook, BeforeExecuteHook, Noop},
 };
 use anyhow::Context;
@@ -25,7 +25,7 @@ use phper::{
     functions::call,
     values::{ExecuteData, ZVal},
 };
-use skywalking::trace::{propagation::encoder::encode_propagation, span::Span};
+use skywalking::trace::span::Span;
 use std::{cell::RefCell, collections::HashMap, os::raw::c_long};
 use tracing::{debug, warn};
 use url::Url;
@@ -404,21 +404,14 @@ impl CurlPlugin {
     }
 
     fn inject_sw_header(request_id: Option<i64>, ch: ZVal, info: &CurlInfo) -> 
crate::Result<()> {
-        let sw_header = RequestContext::try_with_global(request_id, |req_ctx| {
-            let span_object = req_ctx.get_primary_span().span_object();
-            Ok(encode_propagation(
-                &req_ctx.tracing_context,
-                &span_object.operation_name,
-                &span_object.peer,
-            ))
-        })?;
+        let sw_header = RequestContext::try_get_sw_header(request_id)?;
         let mut val = CURL_HEADERS
             .with(|headers| headers.borrow_mut().remove(&info.cid))
             .unwrap_or_else(|| ZVal::from(ZArray::new()));
         if let Some(arr) = val.as_mut_z_arr() {
             arr.insert(
                 InsertKey::NextIndex,
-                ZVal::from(format!("sw8: {}", sw_header)),
+                ZVal::from(format!("{}: {}", SW_HEADER, sw_header)),
             );
             call(
                 "curl_setopt",
diff --git a/src/tag.rs b/src/tag.rs
index 7b0abd1..c408390 100644
--- a/src/tag.rs
+++ b/src/tag.rs
@@ -46,3 +46,7 @@ impl Display for CacheOp {
 
 pub const TAG_DB_STATEMENT: &str = "db.statement";
 pub const TAG_DB_TYPE: &str = "db.type";
+
+pub const TAG_MQ_BROKER: &str = "mq.broker";
+pub const TAG_MQ_TOPIC: &str = "mq.topic";
+pub const TAG_MQ_QUEUE: &str = "mq.queue";
diff --git a/tests/data/expected_context.yaml b/tests/data/expected_context.yaml
index 8438727..7fd7cd2 100644
--- a/tests/data/expected_context.yaml
+++ b/tests/data/expected_context.yaml
@@ -15,7 +15,7 @@
 
 segmentItems:
   - serviceName: skywalking-agent-test-1
-    segmentSize: 16
+    segmentSize: 17
     segments:
       - segmentId: "not null"
         spans:
@@ -1025,6 +1025,68 @@ segmentItems:
               - { key: url, value: "http://127.0.0.1:9011/redis.fail.php"; }
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
+      - segmentId: "not null"
+        spans:
+          - operationName: "PhpAmqpLib\\Channel\\AMQPChannel->basic_publish"
+            parentSpanId: 0
+            spanId: 1
+            spanLayer: MQ
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 144
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:5672
+            skipAnalysis: false
+            tags:
+              - { key: mq.broker, value: "127.0.0.1:5672" }
+              - { key: mq.topic, value: "" }
+              - { key: mq.queue, value: queue_test }
+          - operationName: "PhpAmqpLib\\Channel\\AMQPChannel->basic_publish"
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: MQ
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 144
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:5672
+            skipAnalysis: false
+            tags:
+              - { key: mq.broker, value: "127.0.0.1:5672" }
+              - { key: mq.topic, value: exchange_test }
+              - { key: mq.queue, value: routing_test }
+          - operationName: "PhpAmqpLib\\Channel\\AMQPChannel->basic_publish"
+            parentSpanId: 0
+            spanId: 3
+            spanLayer: MQ
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 144
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:5672
+            skipAnalysis: false
+            tags:
+              - { key: mq.broker, value: "127.0.0.1:5672" }
+              - { key: mq.topic, value: "" }
+              - { key: mq.queue, value: not_exists }
+          - operationName: GET:/rabbitmq.php
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8001
+            isError: false
+            spanType: Entry
+            peer: ""
+            skipAnalysis: false
+            tags:
+              - { key: url, value: "http://127.0.0.1:9011/rabbitmq.php"; }
+              - { key: http.method, value: GET }
+              - { key: http.status_code, value: "200" }
   - serviceName: skywalking-agent-test-2
     segmentSize: 1
     segments:
diff --git a/tests/e2e.rs b/tests/e2e.rs
index d16ae5b..bdf918a 100644
--- a/tests/e2e.rs
+++ b/tests/e2e.rs
@@ -54,6 +54,7 @@ async fn run_e2e() {
     request_fpm_mysqli().await;
     request_fpm_memcached().await;
     request_fpm_redis().await;
+    request_fpm_rabbitmq().await;
     request_swoole_curl().await;
     sleep(Duration::from_secs(3)).await;
     request_collector_validate().await;
@@ -124,6 +125,14 @@ async fn request_fpm_redis() {
     .await;
 }
 
+async fn request_fpm_rabbitmq() {
+    request_common(
+        HTTP_CLIENT.get(format!("http://{}/rabbitmq.php";, 
PROXY_SERVER_1_ADDRESS)),
+        "ok",
+    )
+    .await;
+}
+
 async fn request_swoole_curl() {
     request_common(
         HTTP_CLIENT.get(format!("http://{}/curl";, SWOOLE_SERVER_1_ADDRESS)),
diff --git a/tests/php/composer.json b/tests/php/composer.json
index 082ae6d..a734774 100644
--- a/tests/php/composer.json
+++ b/tests/php/composer.json
@@ -12,6 +12,7 @@
         "ext-swoole": ">=4.5",
         "predis/predis": "^2.0",
         "guzzlehttp/guzzle": "^7.4",
-        "webmozart/assert": "^1.11"
+        "webmozart/assert": "^1.11",
+        "php-amqplib/php-amqplib": "^3.5"
     }
 }
diff --git a/tests/php/composer.lock b/tests/php/composer.lock
index 0e07718..848f723 100644
--- a/tests/php/composer.lock
+++ b/tests/php/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at 
https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies";,
         "This file is @generated automatically"
     ],
-    "content-hash": "c6dee1b36de9a740acdd615d689b6297",
+    "content-hash": "476a32189446bfa8cad519f74dbc3382",
     "packages": [
         {
             "name": "guzzlehttp/guzzle",
@@ -329,6 +329,314 @@
             ],
             "time": "2022-06-20T21:43:11+00:00"
         },
+        {
+            "name": "paragonie/constant_time_encoding",
+            "version": "v2.6.3",
+            "source": {
+                "type": "git",
+                "url": 
"https://github.com/paragonie/constant_time_encoding.git";,
+                "reference": "58c3f47f650c94ec05a151692652a868995d2938"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938";,
+                "reference": "58c3f47f650c94ec05a151692652a868995d2938",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7|^8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6|^7|^8|^9",
+                "vimeo/psalm": "^1|^2|^3|^4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "ParagonIE\\ConstantTime\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "[email protected]",
+                    "homepage": "https://paragonie.com";,
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Steve 'Sc00bz' Thomas",
+                    "email": "[email protected]",
+                    "homepage": "https://www.tobtu.com";,
+                    "role": "Original Developer"
+                }
+            ],
+            "description": "Constant-time Implementations of RFC 4648 Encoding 
(Base-64, Base-32, Base-16)",
+            "keywords": [
+                "base16",
+                "base32",
+                "base32_decode",
+                "base32_encode",
+                "base64",
+                "base64_decode",
+                "base64_encode",
+                "bin2hex",
+                "encoding",
+                "hex",
+                "hex2bin",
+                "rfc4648"
+            ],
+            "support": {
+                "email": "[email protected]",
+                "issues": 
"https://github.com/paragonie/constant_time_encoding/issues";,
+                "source": "https://github.com/paragonie/constant_time_encoding";
+            },
+            "time": "2022-06-14T06:56:20+00:00"
+        },
+        {
+            "name": "paragonie/random_compat",
+            "version": "v9.99.100",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/random_compat.git";,
+                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a";,
+                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">= 7"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*|5.*",
+                "vimeo/psalm": "^1"
+            },
+            "suggest": {
+                "ext-libsodium": "Provides a modern crypto API that can be 
used to generate random bytes."
+            },
+            "type": "library",
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "[email protected]",
+                    "homepage": "https://paragonie.com";
+                }
+            ],
+            "description": "PHP 5.x polyfill for random_bytes() and 
random_int() from PHP 7",
+            "keywords": [
+                "csprng",
+                "polyfill",
+                "pseudorandom",
+                "random"
+            ],
+            "support": {
+                "email": "[email protected]",
+                "issues": "https://github.com/paragonie/random_compat/issues";,
+                "source": "https://github.com/paragonie/random_compat";
+            },
+            "time": "2020-10-15T08:29:30+00:00"
+        },
+        {
+            "name": "php-amqplib/php-amqplib",
+            "version": "v3.5.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-amqplib/php-amqplib.git";,
+                "reference": "bccaaf8ef8bcf18b4ab41e645e92537752b887bd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/php-amqplib/php-amqplib/zipball/bccaaf8ef8bcf18b4ab41e645e92537752b887bd";,
+                "reference": "bccaaf8ef8bcf18b4ab41e645e92537752b887bd",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "ext-sockets": "*",
+                "php": "^7.1||^8.0",
+                "phpseclib/phpseclib": "^2.0|^3.0"
+            },
+            "conflict": {
+                "php": "7.4.0 - 7.4.1"
+            },
+            "replace": {
+                "videlalvaro/php-amqplib": "self.version"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "nategood/httpful": "^0.2.20",
+                "phpunit/phpunit": "^7.5|^9.5",
+                "squizlabs/php_codesniffer": "^3.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpAmqpLib\\": "PhpAmqpLib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "LGPL-2.1-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Alvaro Videla",
+                    "role": "Original Maintainer"
+                },
+                {
+                    "name": "Raúl Araya",
+                    "email": "[email protected]",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Luke Bakken",
+                    "email": "[email protected]",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Ramūnas Dronga",
+                    "email": "[email protected]",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "Formerly videlalvaro/php-amqplib.  This library is 
a pure PHP implementation of the AMQP protocol. It's been tested against 
RabbitMQ.",
+            "homepage": "https://github.com/php-amqplib/php-amqplib/";,
+            "keywords": [
+                "message",
+                "queue",
+                "rabbitmq"
+            ],
+            "support": {
+                "issues": "https://github.com/php-amqplib/php-amqplib/issues";,
+                "source": 
"https://github.com/php-amqplib/php-amqplib/tree/v3.5.3";
+            },
+            "time": "2023-04-03T18:25:49+00:00"
+        },
+        {
+            "name": "phpseclib/phpseclib",
+            "version": "3.0.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpseclib/phpseclib.git";,
+                "reference": "cc181005cf548bfd8a4896383bb825d859259f95"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/phpseclib/phpseclib/zipball/cc181005cf548bfd8a4896383bb825d859259f95";,
+                "reference": "cc181005cf548bfd8a4896383bb825d859259f95",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/constant_time_encoding": "^1|^2",
+                "paragonie/random_compat": "^1.4|^2.0|^9.99.99",
+                "php": ">=5.6.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "*"
+            },
+            "suggest": {
+                "ext-dom": "Install the DOM extension to load XML formatted 
public keys.",
+                "ext-gmp": "Install the GMP (GNU Multiple Precision) extension 
in order to speed up arbitrary precision integer arithmetic operations.",
+                "ext-libsodium": "SSH2/SFTP can make use of some algorithms 
provided by the libsodium-php extension.",
+                "ext-mcrypt": "Install the Mcrypt extension in order to speed 
up a few other cryptographic operations.",
+                "ext-openssl": "Install the OpenSSL extension in order to 
speed up a wide variety of cryptographic operations."
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "phpseclib/bootstrap.php"
+                ],
+                "psr-4": {
+                    "phpseclib3\\": "phpseclib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jim Wigginton",
+                    "email": "[email protected]",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Patrick Monnerat",
+                    "email": "[email protected]",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Andreas Fischer",
+                    "email": "[email protected]",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Hans-Jürgen Petrich",
+                    "email": "[email protected]",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "[email protected]",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP Secure Communications Library - Pure-PHP 
implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+            "homepage": "http://phpseclib.sourceforge.net";,
+            "keywords": [
+                "BigInteger",
+                "aes",
+                "asn.1",
+                "asn1",
+                "blowfish",
+                "crypto",
+                "cryptography",
+                "encryption",
+                "rsa",
+                "security",
+                "sftp",
+                "signature",
+                "signing",
+                "ssh",
+                "twofish",
+                "x.509",
+                "x509"
+            ],
+            "support": {
+                "issues": "https://github.com/phpseclib/phpseclib/issues";,
+                "source": "https://github.com/phpseclib/phpseclib/tree/3.0.19";
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/terrafrost";,
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/phpseclib";,
+                    "type": "patreon"
+                },
+                {
+                    "url": 
"https://tidelift.com/funding/github/packagist/phpseclib/phpseclib";,
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-03-05T17:13:09+00:00"
+        },
         {
             "name": "predis/predis",
             "version": "v2.0.0",
diff --git a/tests/php/fpm/rabbitmq.php b/tests/php/fpm/rabbitmq.php
new file mode 100644
index 0000000..162f8bd
--- /dev/null
+++ b/tests/php/fpm/rabbitmq.php
@@ -0,0 +1,49 @@
+<?php
+
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+use PhpAmqpLib\Connection\AMQPStreamConnection;
+use PhpAmqpLib\Message\AMQPMessage;
+use PhpAmqpLib\Wire\AMQPTable;
+
+
+require_once dirname(__DIR__) . "/vendor/autoload.php";
+
+$connection = new AMQPStreamConnection("127.0.0.1", 5672, 'guest', 'guest');
+$channel = $connection->channel();
+
+$channel->queue_declare('queue_test', false, false, false, false);
+$channel->exchange_declare('exchange_test', 'direct', false, false, false);
+$channel->queue_bind('queue_test', 'exchange_test', 'routing_test');
+
+{
+    $msg = new AMQPMessage('Hello World!');
+    $channel->basic_publish($msg, '', 'queue_test');
+}
+
+{
+    $msg = new AMQPMessage('Hello World!', ['content_type' => 'text/plain']);
+    $channel->basic_publish($msg, 'exchange_test', 'routing_test');
+}
+
+{
+    $msg = new AMQPMessage('Hello World!');
+    $msg->set('application_headers', new AMQPTable(['foo' => 'bar']));
+    $channel->basic_publish($msg, '', 'not_exists');
+}
+
+echo "ok";


Reply via email to