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";