Skip to content

Commit b29bcd5

Browse files
authored
feat(php): implement initial version of PHP SDK (#3235)
1 parent 685d39d commit b29bcd5

32 files changed

Lines changed: 3427 additions & 3 deletions
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: php-pre-merge
19+
description: PHP pre-merge testing github iggy actions
20+
21+
inputs:
22+
task:
23+
description: "Task to run (lint, test, build)"
24+
required: true
25+
26+
runs:
27+
using: "composite"
28+
steps:
29+
- name: Install PHP build dependencies
30+
shell: bash
31+
run: |
32+
sudo apt-get update
33+
sudo apt-get install -y --no-install-recommends \
34+
clang \
35+
composer \
36+
libclang-dev \
37+
libhwloc-dev \
38+
libssl-dev \
39+
php-cli \
40+
php-dev \
41+
php-mbstring \
42+
php-xml \
43+
pkg-config \
44+
unzip
45+
46+
echo "PHP=$(command -v php)" >> "$GITHUB_ENV"
47+
echo "PHP_CONFIG=$(command -v php-config)" >> "$GITHUB_ENV"
48+
php --version
49+
php-config --version
50+
composer --version
51+
52+
- name: Setup Rust with cache
53+
uses: ./.github/actions/utils/setup-rust-with-cache
54+
with:
55+
shared-key: dev
56+
save-cache: "false"
57+
58+
- name: Use shared Cargo target directory
59+
shell: bash
60+
run: echo "CARGO_TARGET_DIR=${GITHUB_WORKSPACE}/target" >> "$GITHUB_ENV"
61+
62+
- name: Validate task
63+
shell: bash
64+
run: |
65+
case "${{ inputs.task }}" in
66+
lint|test|build) ;;
67+
*)
68+
echo "Unknown PHP SDK task: ${{ inputs.task }}"
69+
exit 1
70+
;;
71+
esac
72+
73+
- name: Lint
74+
if: inputs.task == 'lint'
75+
shell: bash
76+
run: |
77+
php -r 'json_decode(file_get_contents("foreign/php/composer.json"), true, 512, JSON_THROW_ON_ERROR);'
78+
cargo fmt --manifest-path foreign/php/Cargo.toml -- --check
79+
cargo install cargo-php --locked
80+
cargo build --manifest-path foreign/php/Cargo.toml
81+
extension="$(find "${CARGO_TARGET_DIR}/debug" -maxdepth 1 -name 'libiggy_php.so' -print -quit)"
82+
if [ -z "$extension" ]; then
83+
echo "PHP extension was not produced"
84+
exit 1
85+
fi
86+
raw_stubs="$(mktemp)"
87+
cargo php stubs "$extension" -o "$raw_stubs"
88+
{
89+
printf '<?php\n'
90+
awk 'NF { print "// " $0; next } { print "//" }' ASF_LICENSE.txt
91+
printf '\n'
92+
tail -n +2 "$raw_stubs"
93+
} > foreign/php/iggy-php.stubs.php
94+
rm -f "$raw_stubs"
95+
git diff --exit-code -- foreign/php/iggy-php.stubs.php
96+
find foreign/php \
97+
-path foreign/php/vendor -prune -o \
98+
-name '*.php' -print0 \
99+
| xargs -0 -n1 php -l
100+
101+
- name: Build PHP extension
102+
if: inputs.task == 'build'
103+
shell: bash
104+
run: |
105+
cargo build --release --manifest-path foreign/php/Cargo.toml
106+
extension="$(find "${CARGO_TARGET_DIR}/release" -maxdepth 1 -name 'libiggy_php.so' -print -quit)"
107+
if [ -z "$extension" ]; then
108+
echo "PHP extension was not produced"
109+
exit 1
110+
fi
111+
ls -lh "$extension"
112+
113+
- name: Build PHP extension for tests
114+
if: inputs.task == 'test'
115+
shell: bash
116+
run: |
117+
cargo build --manifest-path foreign/php/Cargo.toml
118+
extension="$(find "${CARGO_TARGET_DIR}/debug" -maxdepth 1 -name 'libiggy_php.so' -print -quit)"
119+
if [ -z "$extension" ]; then
120+
echo "PHP extension was not produced"
121+
exit 1
122+
fi
123+
echo "PHP_IGGY_EXTENSION=$(realpath "$extension")" >> "$GITHUB_ENV"
124+
ls -lh "$extension"
125+
126+
- name: Install PHP test dependencies
127+
if: inputs.task == 'test'
128+
shell: bash
129+
working-directory: foreign/php
130+
run: composer install --no-interaction --prefer-dist --no-progress
131+
132+
- name: Start Iggy server
133+
if: inputs.task == 'test'
134+
id: iggy
135+
uses: ./.github/actions/utils/server-start
136+
137+
- name: Run PHP SDK tests
138+
if: inputs.task == 'test'
139+
shell: bash
140+
working-directory: foreign/php
141+
env:
142+
IGGY_HOST: 127.0.0.1
143+
IGGY_PORT: 8090
144+
IGGY_USERNAME: iggy
145+
IGGY_PASSWORD: iggy
146+
run: ./scripts/test.sh
147+
148+
- name: Stop Iggy server
149+
if: always() && inputs.task == 'test'
150+
uses: ./.github/actions/utils/server-stop
151+
with:
152+
pid-file: ${{ steps.iggy.outputs.pid_file }}
153+
log-file: ${{ steps.iggy.outputs.log_file }}

.github/config/components.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,15 @@ components:
218218
- "foreign/python/**"
219219
tasks: ["lint", "test", "build"]
220220

221+
sdk-php:
222+
depends_on:
223+
- "rust-sdk" # PHP SDK wraps the Rust SDK
224+
- "rust-server" # For integration tests
225+
- "ci-infrastructure" # CI changes trigger full regression
226+
paths:
227+
- "foreign/php/**"
228+
tasks: ["lint", "test", "build"]
229+
221230
sdk-node:
222231
depends_on:
223232
- "rust-sdk" # Node SDK depends on core SDK

.github/workflows/_detect.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ on:
2626
python_matrix:
2727
description: "Matrix for Python SDK"
2828
value: ${{ jobs.detect.outputs.python_matrix }}
29+
php_matrix:
30+
description: "Matrix for PHP SDK"
31+
value: ${{ jobs.detect.outputs.php_matrix }}
2932
node_matrix:
3033
description: "Matrix for Node SDK"
3134
value: ${{ jobs.detect.outputs.node_matrix }}
@@ -60,6 +63,7 @@ jobs:
6063
outputs:
6164
rust_matrix: ${{ steps.mk.outputs.rust_matrix }}
6265
python_matrix: ${{ steps.mk.outputs.python_matrix }}
66+
php_matrix: ${{ steps.mk.outputs.php_matrix }}
6367
node_matrix: ${{ steps.mk.outputs.node_matrix }}
6468
go_matrix: ${{ steps.mk.outputs.go_matrix }}
6569
java_matrix: ${{ steps.mk.outputs.java_matrix }}
@@ -230,7 +234,7 @@ jobs:
230234
console.log(`Total files changed: ${files.length}`);
231235
}
232236
233-
const groups = { rust:[], python:[], node:[], go:[], java:[], csharp:[], cpp:[], bdd:[], examples:[], other:[] };
237+
const groups = { rust:[], python:[], php:[], node:[], go:[], java:[], csharp:[], cpp:[], bdd:[], examples:[], other:[] };
234238
235239
// Process affected components and generate tasks
236240
console.log('');
@@ -253,6 +257,7 @@ jobs:
253257
254258
if (name === 'rust') groups.rust.push(...entries);
255259
else if (name === 'sdk-python') groups.python.push(...entries);
260+
else if (name === 'sdk-php') groups.php.push(...entries);
256261
else if (name === 'sdk-node') groups.node.push(...entries);
257262
else if (name === 'sdk-go') groups.go.push(...entries);
258263
else if (name === 'sdk-java') groups.java.push(...entries);
@@ -299,6 +304,7 @@ jobs:
299304
// Clear existing groups to avoid duplicates - we'll run everything anyway
300305
groups.rust = [];
301306
groups.python = [];
307+
groups.php = [];
302308
groups.node = [];
303309
groups.go = [];
304310
groups.java = [];
@@ -313,6 +319,7 @@ jobs:
313319
const entries = cfg.tasks.map(task => ({ component: name, task }));
314320
if (name === 'rust') groups.rust.push(...entries);
315321
else if (name === 'sdk-python') groups.python.push(...entries);
322+
else if (name === 'sdk-php') groups.php.push(...entries);
316323
else if (name === 'sdk-node') groups.node.push(...entries);
317324
else if (name === 'sdk-go') groups.go.push(...entries);
318325
else if (name === 'sdk-java') groups.java.push(...entries);
@@ -349,6 +356,7 @@ jobs:
349356
const jobSummary = [
350357
{ name: 'Rust', tasks: groups.rust },
351358
{ name: 'Python SDK', tasks: groups.python },
359+
{ name: 'PHP SDK', tasks: groups.php },
352360
{ name: 'Node SDK', tasks: groups.node },
353361
{ name: 'Go SDK', tasks: groups.go },
354362
{ name: 'Java SDK', tasks: groups.java },
@@ -381,6 +389,7 @@ jobs:
381389
382390
setOutput('rust_matrix', JSON.stringify(matrix(groups.rust)));
383391
setOutput('python_matrix', JSON.stringify(matrix(groups.python)));
392+
setOutput('php_matrix', JSON.stringify(matrix(groups.php)));
384393
setOutput('node_matrix', JSON.stringify(matrix(groups.node)));
385394
setOutput('go_matrix', JSON.stringify(matrix(groups.go)));
386395
setOutput('java_matrix', JSON.stringify(matrix(groups.java)));

.github/workflows/_test.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ jobs:
9797
verbose: true
9898
override_pr: ${{ github.event.pull_request.number }}
9999

100+
# PHP SDK
101+
- name: Run PHP SDK task
102+
if: inputs.component == 'sdk-php'
103+
uses: ./.github/actions/php/pre-merge
104+
with:
105+
task: ${{ inputs.task }}
106+
100107
# Node SDK
101108
- name: Run Node SDK task
102109
if: inputs.component == 'sdk-node'

.github/workflows/pr-title.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ jobs:
7575
mcp
7676
node
7777
partitions
78+
php
7879
proc
7980
pr_template
8081
python

.github/workflows/pre-merge.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ jobs:
7878
secrets:
7979
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
8080

81+
# PHP SDK
82+
test-php:
83+
name: PHP • ${{ matrix.task }}
84+
needs: detect
85+
if: ${{ fromJson(needs.detect.outputs.php_matrix).include[0].component != 'noop' }}
86+
strategy:
87+
fail-fast: false
88+
matrix: ${{ fromJson(needs.detect.outputs.php_matrix) }}
89+
uses: ./.github/workflows/_test.yml
90+
with:
91+
component: ${{ matrix.component }}
92+
task: ${{ matrix.task }}
93+
8194
# Node SDK
8295
test-node:
8396
name: Node • ${{ matrix.task }}
@@ -193,7 +206,7 @@ jobs:
193206
# Final status check
194207
finalize_pr:
195208
runs-on: ubuntu-latest
196-
needs: [common, detect, test-rust, test-python, test-node, test-go, test-java, test-csharp, test-cpp, test-bdd, test-examples, test-other]
209+
needs: [common, detect, test-rust, test-python, test-php, test-node, test-go, test-java, test-csharp, test-cpp, test-bdd, test-examples, test-other]
197210
if: ${{ !cancelled() }}
198211
steps:
199212
- name: Get job execution times
@@ -271,6 +284,7 @@ jobs:
271284
// Set outputs for each component
272285
const rust = findJobInfo('Rust •');
273286
const python = findJobInfo('Python •');
287+
const php = findJobInfo('PHP •');
274288
const node = findJobInfo('Node •');
275289
const go = findJobInfo('Go •');
276290
const java = findJobInfo('Java •');
@@ -291,6 +305,7 @@ jobs:
291305
// Output formatted durations
292306
core.setOutput('rust_time', formatJobDuration(rust));
293307
core.setOutput('python_time', formatJobDuration(python));
308+
core.setOutput('php_time', formatJobDuration(php));
294309
core.setOutput('node_time', formatJobDuration(node));
295310
core.setOutput('go_time', formatJobDuration(go));
296311
core.setOutput('java_time', formatJobDuration(java));
@@ -381,6 +396,7 @@ jobs:
381396
# Language/component tests
382397
rust_status=$(format_status "${{ needs.test-rust.result }}" "${{ steps.times.outputs.rust_time }}")
383398
python_status=$(format_status "${{ needs.test-python.result }}" "${{ steps.times.outputs.python_time }}")
399+
php_status=$(format_status "${{ needs.test-php.result }}" "${{ steps.times.outputs.php_time }}")
384400
node_status=$(format_status "${{ needs.test-node.result }}" "${{ steps.times.outputs.node_time }}")
385401
go_status=$(format_status "${{ needs.test-go.result }}" "${{ steps.times.outputs.go_time }}")
386402
java_status=$(format_status "${{ needs.test-java.result }}" "${{ steps.times.outputs.java_time }}")
@@ -392,6 +408,7 @@ jobs:
392408
393409
echo "| 🦀 Rust | $rust_status | ${{ steps.times.outputs.rust_time }} |" >> $GITHUB_STEP_SUMMARY
394410
echo "| 🐍 Python | $python_status | ${{ steps.times.outputs.python_time }} |" >> $GITHUB_STEP_SUMMARY
411+
echo "| 🐘 PHP | $php_status | ${{ steps.times.outputs.php_time }} |" >> $GITHUB_STEP_SUMMARY
395412
echo "| 🟢 Node | $node_status | ${{ steps.times.outputs.node_time }} |" >> $GITHUB_STEP_SUMMARY
396413
echo "| 🐹 Go | $go_status | ${{ steps.times.outputs.go_time }} |" >> $GITHUB_STEP_SUMMARY
397414
echo "| ☕ Java | $java_status | ${{ steps.times.outputs.java_time }} |" >> $GITHUB_STEP_SUMMARY
@@ -408,6 +425,7 @@ jobs:
408425
[[ "${{ needs.detect.result }}" == "failure" ]] || \
409426
[[ "${{ needs.test-rust.result }}" == "failure" ]] || \
410427
[[ "${{ needs.test-python.result }}" == "failure" ]] || \
428+
[[ "${{ needs.test-php.result }}" == "failure" ]] || \
411429
[[ "${{ needs.test-node.result }}" == "failure" ]] || \
412430
[[ "${{ needs.test-go.result }}" == "failure" ]] || \
413431
[[ "${{ needs.test-java.result }}" == "failure" ]] || \
@@ -423,6 +441,7 @@ jobs:
423441
[[ "${{ needs.detect.result }}" == "cancelled" ]] || \
424442
[[ "${{ needs.test-rust.result }}" == "cancelled" ]] || \
425443
[[ "${{ needs.test-python.result }}" == "cancelled" ]] || \
444+
[[ "${{ needs.test-php.result }}" == "cancelled" ]] || \
426445
[[ "${{ needs.test-node.result }}" == "cancelled" ]] || \
427446
[[ "${{ needs.test-go.result }}" == "cancelled" ]] || \
428447
[[ "${{ needs.test-java.result }}" == "cancelled" ]] || \

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ members = [
6262
"core/tools",
6363
"examples/rust",
6464
]
65-
exclude = ["foreign/cpp", "foreign/python"]
65+
exclude = ["foreign/cpp", "foreign/php", "foreign/python"]
6666
resolver = "2"
6767

6868
[workspace.dependencies]

foreign/php/.cargo/config.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
[target.aarch64-apple-darwin]
19+
rustflags = ["-C", "link-arg=-Wl,-undefined,dynamic_lookup"]
20+
21+
[target.x86_64-apple-darwin]
22+
rustflags = ["-C", "link-arg=-Wl,-undefined,dynamic_lookup"]

foreign/php/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
/vendor
3+
Cargo.lock
4+
composer.lock

0 commit comments

Comments
 (0)