This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new 2c286679a feat(php): add SDK examples component (#3368)
2c286679a is described below
commit 2c286679a28994e09509aaeba5f1a563b6326e44
Author: Diaconu Radu-Mihai <[email protected]>
AuthorDate: Fri May 29 16:13:01 2026 +0300
feat(php): add SDK examples component (#3368)
---
.github/config/components.yml | 12 ++++
.github/workflows/_test_examples.yml | 41 ++++++++++++++
examples/php/README.md | 39 +++++++++++++
examples/php/basic/consumer.php | 31 +++++++++++
examples/php/basic/producer.php | 33 +++++++++++
examples/php/getting-started/consumer.php | 31 +++++++++++
examples/php/getting-started/producer.php | 33 +++++++++++
examples/php/src/common.php | 91 +++++++++++++++++++++++++++++++
scripts/run-examples-from-readme.sh | 48 +++++++++++++++-
9 files changed, 356 insertions(+), 3 deletions(-)
diff --git a/.github/config/components.yml b/.github/config/components.yml
index 0e20b897a..fea06a63a 100644
--- a/.github/config/components.yml
+++ b/.github/config/components.yml
@@ -402,6 +402,18 @@ components:
- "scripts/utils.sh"
tasks: ["examples-python"]
+ examples-php:
+ depends_on:
+ - "rust-server"
+ - "rust-sdk" # All SDKs depend on core SDK changes
+ - "sdk-php"
+ - "ci-infrastructure"
+ paths:
+ - "examples/php/**"
+ - "scripts/run-examples-from-readme.sh"
+ - "scripts/utils.sh"
+ tasks: ["examples-php"]
+
examples-node:
depends_on:
- "rust-server"
diff --git a/.github/workflows/_test_examples.yml
b/.github/workflows/_test_examples.yml
index 01454e1f5..28942a81e 100644
--- a/.github/workflows/_test_examples.yml
+++ b/.github/workflows/_test_examples.yml
@@ -65,6 +65,29 @@ jobs:
if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-python'
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b #
v8.1.0
+ - name: Install PHP build dependencies
+ if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-php'
+ shell: bash
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends \
+ clang \
+ libclang-dev \
+ libhwloc-dev \
+ libssl-dev \
+ php8.3-cli \
+ php8.3-dev \
+ pkg-config
+
+ php_bin="$(command -v php8.3)"
+ php_config="$(command -v php-config8.3)"
+ {
+ echo "PHP=${php_bin}"
+ echo "PHP_CONFIG=${php_config}"
+ } >> "$GITHUB_ENV"
+ "${php_bin}" --version
+ "${php_config}" --version
+
- name: Cache uv
if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-python'
uses: actions/cache@v5
@@ -123,6 +146,20 @@ jobs:
fi
fi
+ - name: Build PHP extension for examples
+ if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-php'
+ shell: bash
+ run: |
+ export CARGO_TARGET_DIR="${GITHUB_WORKSPACE}/target"
+ cargo build --manifest-path foreign/php/Cargo.toml
+ extension="$(find target/debug -maxdepth 1 -name 'libiggy_php.so'
-print -quit)"
+ if [ -z "$extension" ]; then
+ echo "PHP extension was not produced"
+ exit 1
+ fi
+ echo "PHP_IGGY_EXTENSION=$(realpath "$extension")" >> "$GITHUB_ENV"
+ ls -lh "$extension"
+
- name: Build Node SDK for examples
if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-node'
run: |
@@ -150,6 +187,10 @@ jobs:
if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-python'
run: ./scripts/run-examples-from-readme.sh --language python
+ - name: Run PHP examples
+ if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-php'
+ run: ./scripts/run-examples-from-readme.sh --language php
+
- name: Run Node.js examples
if: startsWith(inputs.component, 'examples-') && inputs.task ==
'examples-node'
run: ./scripts/run-examples-from-readme.sh --language node
diff --git a/examples/php/README.md b/examples/php/README.md
new file mode 100644
index 000000000..58e0abde6
--- /dev/null
+++ b/examples/php/README.md
@@ -0,0 +1,39 @@
+# Iggy PHP Examples
+
+This directory contains sample applications that show how to use the Apache
+Iggy PHP SDK extension.
+
+## Running Examples
+
+Start the server from the repository root:
+
+```bash
+cargo run --bin iggy-server -- --fresh --with-default-root-credentials
+```
+
+From `examples/php`, build the PHP extension and point PHP at it:
+
+```bash
+(cd ../../foreign/php && cargo build)
+export
PHP_IGGY_EXTENSION="$(pwd)/../../foreign/php/target/debug/libiggy_php.so"
+```
+
+On macOS, use `../../foreign/php/target/debug/libiggy_php.dylib` instead.
+
+### Getting Started
+
+```bash
+php -d
extension="${PHP_IGGY_EXTENSION:-../../foreign/php/target/debug/libiggy_php.so}"
getting-started/producer.php
+php -d
extension="${PHP_IGGY_EXTENSION:-../../foreign/php/target/debug/libiggy_php.so}"
getting-started/consumer.php
+```
+
+### Basic Usage
+
+```bash
+php -d
extension="${PHP_IGGY_EXTENSION:-../../foreign/php/target/debug/libiggy_php.so}"
basic/producer.php
+php -d
extension="${PHP_IGGY_EXTENSION:-../../foreign/php/target/debug/libiggy_php.so}"
basic/consumer.php
+```
+
+The examples use `IGGY_CONNECTION_STRING` when it is set. Otherwise they build
+`iggy+tcp://iggy:[email protected]:8090` from `IGGY_HOST`, `IGGY_PORT`,
+`IGGY_USERNAME`, and `IGGY_PASSWORD`, which can be set individually.
diff --git a/examples/php/basic/consumer.php b/examples/php/basic/consumer.php
new file mode 100644
index 000000000..81c61870e
--- /dev/null
+++ b/examples/php/basic/consumer.php
@@ -0,0 +1,31 @@
+<?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.
+ */
+
+declare(strict_types=1);
+
+require_once __DIR__ . '/../src/common.php';
+
+$stream = 'php-basic-stream';
+$topic = 'php-basic-topic';
+$client = iggy_client();
+
+ensure_stream_and_topic($client, $stream, $topic);
+print_polled_messages($client, $stream, $topic, 3);
diff --git a/examples/php/basic/producer.php b/examples/php/basic/producer.php
new file mode 100644
index 000000000..eac864333
--- /dev/null
+++ b/examples/php/basic/producer.php
@@ -0,0 +1,33 @@
+<?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.
+ */
+
+declare(strict_types=1);
+
+require_once __DIR__ . '/../src/common.php';
+
+$stream = 'php-basic-stream';
+$topic = 'php-basic-topic';
+$client = iggy_client();
+
+ensure_stream_and_topic($client, $stream, $topic);
+send_payloads($client, $stream, $topic, example_payloads('basic', 3));
+
+echo "Sent 3 PHP basic messages", PHP_EOL;
diff --git a/examples/php/getting-started/consumer.php
b/examples/php/getting-started/consumer.php
new file mode 100644
index 000000000..fee42b9de
--- /dev/null
+++ b/examples/php/getting-started/consumer.php
@@ -0,0 +1,31 @@
+<?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.
+ */
+
+declare(strict_types=1);
+
+require_once __DIR__ . '/../src/common.php';
+
+$stream = 'php-getting-started-stream';
+$topic = 'php-getting-started-topic';
+$client = iggy_client();
+
+ensure_stream_and_topic($client, $stream, $topic);
+print_polled_messages($client, $stream, $topic, 5);
diff --git a/examples/php/getting-started/producer.php
b/examples/php/getting-started/producer.php
new file mode 100644
index 000000000..f0ab90486
--- /dev/null
+++ b/examples/php/getting-started/producer.php
@@ -0,0 +1,33 @@
+<?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.
+ */
+
+declare(strict_types=1);
+
+require_once __DIR__ . '/../src/common.php';
+
+$stream = 'php-getting-started-stream';
+$topic = 'php-getting-started-topic';
+$client = iggy_client();
+
+ensure_stream_and_topic($client, $stream, $topic);
+send_payloads($client, $stream, $topic, example_payloads('getting-started',
5));
+
+echo "Sent 5 PHP getting-started messages", PHP_EOL;
diff --git a/examples/php/src/common.php b/examples/php/src/common.php
new file mode 100644
index 000000000..a14a3a02b
--- /dev/null
+++ b/examples/php/src/common.php
@@ -0,0 +1,91 @@
+<?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.
+ */
+
+declare(strict_types=1);
+
+use Iggy\Client;
+use Iggy\PollingStrategy;
+use Iggy\SendMessage;
+
+function iggy_connection_string(): string
+{
+ $configured = getenv('IGGY_CONNECTION_STRING');
+ if ($configured !== false && $configured !== '') {
+ return $configured;
+ }
+
+ $host = getenv('IGGY_HOST') ?: '127.0.0.1';
+ $port = getenv('IGGY_PORT') ?: '8090';
+ $username = rawurlencode(getenv('IGGY_USERNAME') ?: 'iggy');
+ $password = rawurlencode(getenv('IGGY_PASSWORD') ?: 'iggy');
+
+ return "iggy+tcp://{$username}:{$password}@{$host}:{$port}";
+}
+
+function iggy_client(): Client
+{
+ $client = Client::fromConnectionString(iggy_connection_string());
+ $client->connect();
+
+ return $client;
+}
+
+function ensure_stream_and_topic(Client $client, string $stream, string
$topic): void
+{
+ if ($client->getStream($stream) === null) {
+ $client->createStream($stream);
+ }
+
+ if ($client->getTopic($stream, $topic) === null) {
+ $client->createTopic($stream, $topic, 1);
+ }
+}
+
+function send_payloads(Client $client, string $stream, string $topic, array
$payloads): void
+{
+ $messages = array_map(
+ static fn (string $payload): SendMessage => new SendMessage($payload),
+ $payloads,
+ );
+
+ $client->sendMessages($stream, $topic, 0, $messages);
+}
+
+function print_polled_messages(Client $client, string $stream, string $topic,
int $count): void
+{
+ $messages = $client->pollMessages($stream, $topic, 0,
PollingStrategy::first(), $count, true);
+
+ if (count($messages) < $count) {
+ throw new RuntimeException("Expected {$count} messages, received " .
count($messages));
+ }
+
+ foreach ($messages as $message) {
+ echo "Received message at offset {$message->offset()}:
{$message->payload()}", PHP_EOL;
+ }
+}
+
+function example_payloads(string $prefix, int $count): array
+{
+ return array_map(
+ static fn (int $index): string => "{$prefix}-message-{$index}",
+ range(1, $count),
+ );
+}
diff --git a/scripts/run-examples-from-readme.sh
b/scripts/run-examples-from-readme.sh
index 35c8dc9c6..8567c6d05 100755
--- a/scripts/run-examples-from-readme.sh
+++ b/scripts/run-examples-from-readme.sh
@@ -22,7 +22,7 @@ set -euo pipefail
# Unified script to run SDK examples from README.md files.
# Usage: ./scripts/run-examples-from-readme.sh [OPTIONS]
#
-# --language LANG Language to test: rust|go|node|python|java|csharp
(default: all)
+# --language LANG Language to test: rust|go|node|python|php|java|csharp
(default: all)
# --target TARGET Cargo target architecture for the server binary
# --skip-tls Skip TLS example tests
#
@@ -284,6 +284,47 @@ run_python_examples() {
return "${EXAMPLES_EXIT_CODE}"
}
+# shellcheck disable=SC2329
+run_php_examples() {
+ resolve_server_binary "${TARGET}"
+
+ local php_bin="${PHP:-php}"
+ if [ -z "${PHP_IGGY_EXTENSION:-}" ]; then
+ local extension_candidate
+ for extension_candidate in \
+ target/debug/libiggy_php.so \
+ target/debug/libiggy_php.dylib \
+ foreign/php/target/debug/libiggy_php.so \
+ foreign/php/target/debug/libiggy_php.dylib; do
+ if [ -f "${extension_candidate}" ]; then
+ PHP_IGGY_EXTENSION="$(pwd)/${extension_candidate}"
+ break
+ fi
+ done
+ export PHP_IGGY_EXTENSION
+ fi
+
+ if [ -z "${PHP_IGGY_EXTENSION:-}" ] || [ ! -f "${PHP_IGGY_EXTENSION}" ];
then
+ echo "Error: PHP extension not found. Build foreign/php first or set
PHP_IGGY_EXTENSION."
+ exit 1
+ fi
+
+ TRANSFORM_COMMAND() {
+ local command="$1"
+ printf '%s\n' "${command/#php /\"${php_bin}\" }"
+ }
+
+ run_language_examples \
+ "PHP" \
+ "examples/php" \
+ "README.md" \
+ "^php " \
+ "" \
+ "" \
+ 0 \
+ "--fresh"
+}
+
# shellcheck disable=SC2329
run_java_examples() {
resolve_server_binary "${TARGET}"
@@ -355,16 +396,17 @@ case "${LANGUAGE}" in
node) run_one run_node_examples "Node" ;;
go) run_one run_go_examples "Go" ;;
python) run_one run_python_examples "Python" ;;
+ php) run_one run_php_examples "PHP" ;;
java) run_one run_java_examples "Java" ;;
csharp) run_one run_csharp_examples "C#" ;;
all)
- for lang in rust node go python java csharp; do
+ for lang in rust node go python php java csharp; do
run_one "run_${lang}_examples" "${lang}"
done
;;
*)
echo "Unknown language: ${LANGUAGE}"
- echo "Supported: rust, node, go, python, java, csharp, all"
+ echo "Supported: rust, node, go, python, php, java, csharp, all"
exit 1
;;
esac