Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
53f8d62
refactor: migrate to Zend Memory API, add comprehensive tests, fix st…
xMlex Jul 1, 2026
b8996c1
Remove unnecessary <zend.h> include from tr_network.h
xMlex Jul 1, 2026
ae8c889
Add php.h stub for standalone unit tests
xMlex Jul 1, 2026
d951282
Update README: mark request_id as done, remove local path
xMlex Jul 1, 2026
94a3665
Rewrite README: build instructions, function reference, quick start
xMlex Jul 1, 2026
1324db0
Add install test script and fix Dockerfile
xMlex Jul 1, 2026
5acbd4d
Add overflow checks in tr_array ensure_capacity and init
xMlex Jul 1, 2026
b35199b
Update README: add INI config, how-it-works, remove TODO and .travis.yml
xMlex Jul 1, 2026
678eb1b
add opencode
xMlex Jul 1, 2026
4037b4e
Fix size tracking in tr_array write helpers after set_position
xMlex Jul 1, 2026
3e097ce
Add tests for size tracking after set_position + writes
xMlex Jul 1, 2026
fa9d4d3
Fix NULL/overflow safety in parse_domain_port_pairs
xMlex Jul 1, 2026
8b5c752
Fix docs and install test for trochilidae_timer_get_info()
xMlex Jul 1, 2026
baa8f30
Fix memory leaks and unsafe patterns
xMlex Jul 1, 2026
6ca0f9e
Add UDP test server for E2E integration testing
xMlex Jul 1, 2026
328ab3c
Generate random request_id for CLI mode
xMlex Jul 1, 2026
76b9349
Use uint64_t for generate_random_ulong and seed srand fallback
xMlex Jul 1, 2026
bedd581
Add internal hooks system for curl_exec and SoapClient->__soapCall
xMlex Jul 1, 2026
149eb69
Add trochilidae.chunk_size INI (default 65507, clamps to MAX_CHUNK_SIZE)
xMlex Jul 1, 2026
09c98b1
Add docs/protocol.md with wire format specification
xMlex Jul 1, 2026
d5b14bd
Add protocol TODO section to README.md
xMlex Jul 1, 2026
93d35f2
Update TODO: add code stability/scalability items alongside protocol …
xMlex Jul 1, 2026
21da586
S1: Move setsockopt SO_SNDBUF to init, set to 256KB
xMlex Jul 2, 2026
459eeb9
S2: Replace gethostbyname with getaddrinfo (thread-safe DNS)
xMlex Jul 2, 2026
e0aab17
S6: Fix DNS refresh guard condition
xMlex Jul 2, 2026
101c871
S11: Mark as by design — totalSentSize counts wire bytes
xMlex Jul 2, 2026
42ef668
S9: Heap-allocate chunk packet instead of stack
xMlex Jul 2, 2026
022e333
S7: Replace fprintf(stderr)/perror with php_error_docref
xMlex Jul 2, 2026
af4526e
S8: Use client->chunk_count in error message
xMlex Jul 2, 2026
03118af
S3+S4: Non-blocking sendto with drop counter
xMlex Jul 2, 2026
dbd92b2
Add chunking test (004_chunking)
xMlex Jul 2, 2026
e54de32
S5: Replace strcpy/strncpy with strlcpy/bounded memcpy
xMlex Jul 2, 2026
9ee5338
P1+P3+P6: Magic+version and payload_len in chunk header (reusing rese…
xMlex Jul 2, 2026
bfe87fa
Update protocol.md: chunk header now has magic+version+payload_len
xMlex Jul 2, 2026
5e998b6
update readme
xMlex Jul 2, 2026
72d0b33
fix build and tests
xMlex Jul 2, 2026
3c9309b
fix build and tests
xMlex Jul 2, 2026
9935a9b
fix: revert tr_network.c to system allocator, fix stubs and instructions
xMlex Jul 2, 2026
68a562a
refactor: replace tests/stubs/php.h with trochilidae/compat.h
xMlex Jul 2, 2026
96f38a9
add e2e test instructions to .opencode/instructions.md
xMlex Jul 2, 2026
8cb92c0
fix build and tests
xMlex Jul 2, 2026
00f06b0
fix alpine build
xMlex Jul 20, 2026
f501065
fix alpine build
xMlex Jul 20, 2026
8c33bda
run tests on alpine
xMlex Jul 20, 2026
611267c
[alpine] fix ./trochilidae/tr_timer.h:17:5: error: unknown type name …
xMlex Jul 20, 2026
6e1d79d
fix collector lifecycle and chunk send failure handling
xMlex Jul 20, 2026
1ce78dd
fix timestamp serialization and defensive request metrics encoding
xMlex Jul 20, 2026
9de6f08
harden network host checks and align protocol validation
xMlex Jul 20, 2026
39223bf
restore patched handlers during module shutdown
xMlex Jul 20, 2026
8e06f14
fix fpm request lifecycle memory handling and add repro coverage
xMlex Jul 20, 2026
3035f10
add alpine-based cli/fpm stress and debug test harness
xMlex Jul 20, 2026
0c7952a
add problematic send metric to phpinfo output
xMlex Jul 20, 2026
5243cd1
[alpine] fix hooks
xMlex Jul 20, 2026
cf0d328
[alpine] fix rm: can't remove '/tmp/tmp.AFKNdC/php-fpm.pid': No such …
xMlex Jul 20, 2026
17c1d50
Fix chunk_size INI ordering and document build/test workflow.
xMlex Aug 14, 2026
6887dd2
Fix hooks test on PHP 7.4 where json is not loaded by run-tests.
xMlex Aug 14, 2026
5fcd8a2
Fix request data string leak on repeated trochilidae_reset
xMlex Sep 17, 2026
4e04945
fix
xMlex Sep 17, 2026
ec97ffd
composer for pie
xMlex Sep 18, 2026
d112142
composer for pie
xMlex Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,53 @@ jobs:
run: ./configure --enable-trochilidae
- name: make
run: make
- name: run test.php
run: php -d extension=`pwd`/modules/trochilidae.so -f test.php
- name: make test
run: make test
- name: Compile unit tests
run: gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -D_GNU_SOURCE -g -O0 tests/unit_test.c trochilidae/tr_network.c trochilidae/utils.c -o tests/unit_test
- name: Run unit tests
run: ./tests/unit_test
- name: Compile internal unit tests (tr_array, tr_timer)
run: gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -g -O0 -Wall tests/tr_internal_test.c -o tests/tr_internal_test -lm
- name: Run internal unit tests
run: ./tests/tr_internal_test

build-alpine:
name: Trochilidae Alpine (PHP ${{ matrix.php-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.5']

container:
image: php:${{ matrix.php-version }}-fpm-alpine

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install build dependencies
run: apk add --no-cache $PHPIZE_DEPS linux-headers bash fcgi ripgrep

- name: phpize
run: phpize

- name: configure
run: ./configure --enable-trochilidae

- name: make
run: make -j"$(nproc)"

- name: make test
run: make test
- name: Compile unit tests
run: gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -D_GNU_SOURCE -g -O0 tests/unit_test.c trochilidae/tr_network.c trochilidae/utils.c -o tests/unit_test
- name: Run unit tests
run: ./tests/unit_test
- name: Compile internal unit tests (tr_array, tr_timer)
run: gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -g -O0 -Wall tests/tr_internal_test.c -o tests/tr_internal_test -lm
- name: Run internal unit tests
run: ./tests/tr_internal_test
- name: Run FPM repro test
run: bash tests/fpm_repro_test.sh
33 changes: 33 additions & 0 deletions .github/workflows/opencode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: opencode

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]

jobs:
opencode:
if: |
contains(github.event.comment.body, ' /oc') ||
startsWith(github.event.comment.body, '/oc') ||
contains(github.event.comment.body, ' /opencode') ||
startsWith(github.event.comment.body, '/opencode')
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: read
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
Comment on lines +23 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Описание: Проверка последних версий (тегов) для actions/checkout
gh api repos/actions/checkout/tags --jq '.[].name' | head -n 5

Repository: xMlex/php_trochilidae

Length of output: 190


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Показать workflow с номерами строк
if [ -f .github/workflows/opencode.yml ]; then
  cat -n .github/workflows/opencode.yml
else
  echo "File not found"
fi

Repository: xMlex/php_trochilidae

Length of output: 1224


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Проверить, есть ли в workflow явное определение ref/sha для checkout
rg -n "actions/checkout|ref:|pull_request|issue_comment|pull_request_review_comment|head.sha|base.sha|github\.event" .github/workflows/opencode.yml

Repository: xMlex/php_trochilidae

Length of output: 483


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n .github/workflows/opencode.yml

Repository: xMlex/php_trochilidae

Length of output: 1224


🌐 Web query:

GitHub Actions issue_comment pull_request_review_comment GITHUB_REF default ref checkout behavior docs

💡 Result:

When using actions/checkout in GitHub Actions, the default behavior for the ref input is to check out the reference or SHA that triggered the workflow [1][2]. If no specific reference is provided by the triggering event, it falls back to the repository's default branch [1][2]. For events like issue_comment and pull_request_review_comment, GITHUB_REF does not point to the pull request branch by default [3][4]. Instead, these events typically result in GITHUB_REF pointing to the repository's default branch (or, in some configurations, a merge ref) [3][4]. Because actions/checkout uses GITHUB_REF by default, simply using the action without specifying a ref will generally check out the default branch rather than the pull request's code [5][4]. To check out the pull request code when triggered by issue_comment or pull_request_review_comment, you must manually resolve and pass the pull request's head branch or commit SHA to the ref input of actions/checkout [5][4]. A common pattern involves using actions/github-script to fetch the pull request details from the event payload and setting an output variable, which is then used as the ref in the checkout step [5]: - name: Get PR branch id: pr uses: actions/github-script@v7 with: script: | const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.issue.number }); core.setOutput('head_ref', pr.head.ref); - uses: actions/checkout@v4 with: ref: ${{ steps.pr.outputs.head_ref }} Always ensure that your workflow has the necessary permissions (e.g., contents: read and pull-requests: read) to access repository data and interact with the Pull Request API [5].

Citations:


Укажите явный ref для checkout
actions/checkout@v6 здесь корректен, но без ref на issue_comment/pull_request_review_comment workflow может проверить не код PR. Передайте head PR явно.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/opencode.yml around lines 23 - 26, Update the
actions/checkout step in the workflow to provide an explicit ref for the pull
request head, using the event’s PR head reference so issue_comment and
pull_request_review_comment runs check the PR code rather than the default
branch.


- name: Run opencode
uses: anomalyco/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/deepseek-v4-flash-free
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@
!test.php
!/trochilidae
!package.xml
!Dockerfile
!Dockerfile
!/docs
!AGENTS.md
!composer.json

!/trochilidae/compat.h
!/tests/install_test.sh
11 changes: 0 additions & 11 deletions .travis.yml

This file was deleted.

135 changes: 135 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Project Guidelines: php_trochilidae

This project is a C-based PHP extension for metrics collection.

## Project Structure
- `trochilidae/`: Contains core functional sub-components.
- `tr_array.c/h`: Buffer and array manipulation (binary serialization).
- `tr_network.c/h`: TCP/UDP socket communication.
- `tr_timer.c/h`: High-resolution timers and Zend resource management.
- `tr_hooks.c`: Hooks for SAPI integration (intercepting output/execution).
- `utils.c/h`: Helper functions and utility macros.
- `docs/protocol.md`: Wire format specification for the UDP protocol.
- `tests/`: Contains `.phpt` files for integration testing.
- `.github/workflows/build.yml` build and run tests CI

## Core Conventions
- **Naming:**
- Internal functions: `tr_` prefix.
- PHP-exposed functions: `trochilidae_` prefix.
- **State Management:** Use `ZEND_DECLARE_MODULE_GLOBALS` and access globals via the `TR_G()` macro.
- **Resources:** Use Zend resource registration (`zend_register_list_destructors_ex`) for automated lifecycle management.
- **Performance:** Maintain low overhead; use custom binary serialization over JSON where possible.
- **Error Handling:** Use `php_error_docref`.

## Memory Allocator Convention
- **`trochilidae/tr_network.c`** uses the **system allocator** (`malloc`/`free`/`strdup`), NOT Zend allocator. This is a standalone networking utility that must also compile with stubs.
- **`trochilidae/tr_array.c`**, **`trochilidae/tr_timer.c`**, and **`trochilidae/tr_hooks.c`** use the **Zend allocator** (`emalloc`/`efree`/`estrdup`).
- **`trochilidae.c`** uses **both**: data crossing the boundary with `tr_network.c` (`client->host`, `pairs`) uses system allocator to match; PHP-internal data (`request_id`) uses Zend allocator.
- **Callers must match the callee's allocator**: if `parse_domain_port_pairs` allocates with `malloc`, the caller must free with `free` (not `efree`). Never mix allocators for the same pointer.
- **Test stubs** (`trochilidae/compat.h`): compile with `-DTROCHILIDAE_STANDALONE` to map `emalloc → malloc`, `efree → free`, `estrdup → strdup` for unit tests outside PHP.

## Build & Test Workflow
1. **Configuration:** `phpize && ./configure --enable-trochilidae`
2. **Compilation:** `make`
3. **PHP .phpt tests:**
`php -d extension=modules/trochilidae.so run-tests.php -d extension=modules/trochilidae.so tests/`
*Note:* The `-d extension=...` flag must be passed **both** to `run-tests.php` (so it propagates to child processes) and to the parent process for the skip checks.
4. **C unit tests (network):**
`gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -D_GNU_SOURCE -g -O0 tests/unit_test.c trochilidae/tr_network.c trochilidae/utils.c -o tests/unit_test -lm && ./tests/unit_test`
5. **C unit tests (internal — tr_array, tr_timer):**
`gcc -I. -DTROCHILIDAE_STANDALONE -DHAVE_CONFIG_H -g -O0 -Wall tests/tr_internal_test.c -o tests/tr_internal_test -lm && ./tests/tr_internal_test`
*Note:* Requires `config.h` from `./configure`. Uses `trochilidae/compat.h` with `-DTROCHILIDAE_STANDALONE` instead of real PHP headers.

6. **Docker FPM repro test** (recommended for FPM crash validation):
`tests/run_fpm_repro_in_docker.sh`
- Builds extension inside `php:8.5-fpm-alpine`.
- Runs `.phpt` tests and then `tests/fpm_repro_test.sh` in FPM mode.
- Expected success output includes: `FPM repro test passed`.

### E2E Tests (UDP protocol validation)

E2E tests verify that the extension correctly sends binary UDP packets (chunked protocol) by directing traffic to a local UDP test server that parses and validates the wire format.

1. **Start the UDP test server:**
```bash
php -d extension=modules/trochilidae.so tests/udp_test_server.php --port=30002 --timeout=3 --verbose
```
- Listens on `udp://0.0.0.0:30002` for chunked trochilidae packets.
- Reassembles chunks, parses the binary protocol, and prints the decoded fields.
- Exit code `0` — packet received and valid; `1` — timeout/error.
- Supports `--expect=FILE.json` for deterministic field validation.

2. **Run PHP scripts that send metrics via the extension:**
```bash
php -d extension=modules/trochilidae.so -d "trochilidae.server_list=127.0.0.1:30002" \
-r 'trochilidae_set_tag("e2e", "test"); trochilidae_flush();'
```

3. **Full automated E2E cycle** (as used in `tests/install_test.sh`):
```bash
UDP_PORT=30008
UDP_LOG=$(mktemp)
php -d extension=modules/trochilidae.so tests/udp_test_server.php \
--port=$UDP_PORT --timeout=3 --verbose > "$UDP_LOG" 2>&1 &
UDP_PID=$!
# wait for "ready" in log, then run test PHP scripts
php -d extension=modules/trochilidae.so -d "trochilidae.server_list=127.0.0.1:$UDP_PORT" \
-r 'trochilidae_set_tag("t", "1"); trochilidae_flush();'
sleep 0.5
kill $UDP_PID 2>/dev/null; wait $UDP_PID 2>/dev/null || true
grep -q "=== Parsed packet ===" "$UDP_LOG" && echo "E2E PASS" || echo "E2E FAIL"
rm -f "$UDP_LOG"
```

4. **Multi-chunk E2E test** (verifies chunking with small `chunk_size`):

```bash
UDP_PORT=30009
UDP_LOG=$(mktemp)
php -d extension=modules/trochilidae.so tests/udp_test_server.php \
--port=$UDP_PORT --timeout=3 --verbose > "$UDP_LOG" 2>&1 &
UDP_PID=$!
sleep 0.5
php -d extension=modules/trochilidae.so \
-d trochilidae.chunk_size=120 \
-d "trochilidae.server_list=127.0.0.1:$UDP_PORT" \
-r '
for ($i = 0; $i < 50; $i++) {
trochilidae_set_tag("k$i", str_repeat("x", 25));
}
trochilidae_flush();
'
sleep 0.8
kill $UDP_PID 2>/dev/null; wait $UDP_PID 2>/dev/null || true
chunks=$(grep -c "Received chunk" "$UDP_LOG")
echo "Chunks: $chunks"
grep -q "All chunks received" "$UDP_LOG" && echo "MULTI-CHUNK PASS" || echo "FAIL"
rm -f "$UDP_LOG"
```
Expected: ~21 chunks (99 bytes payload each with `chunk_size=120`, since `CHUNK_HEADER_SIZE=21`).

**Key points:**
- Tests that run with a real `trochilidae.server_list` setting emit UDP packets — those packets go to the configured server.
- For isolated .phpt tests that don't need a server, set `trochilidae.server_list=localhost` (the packets will be sent but never arrive, which is harmless).
- For full protocol validation, use the UDP test server as shown above.

### FPM Repro Scripts (brief)

- `tests/fpm_repro_test.sh` — launches local php-fpm + UDP server, sends multiple FastCGI requests, fails on crash signatures (`zend_mm_heap corrupted`, `SIGABRT`, `SIGSEGV`) or UDP absence.
- `tests/fpm_repro_request.php` — request script with tag/timer activity and `$_SERVER` mutations (main FPM stress scenario).
- `tests/fpm_min_request.php` — minimal control request script (`echo` only).

Quick usage:

```bash
# full run (build + phpt + fpm repro) in Docker
tests/run_fpm_repro_in_docker.sh

# run only FPM repro in container with minimal request script
docker run --rm -e REQUEST_SCRIPT=/work/tests/fpm_min_request.php \
trochilidae-fpm-repro:local bash -lc '/work/tests/fpm_repro_test.sh'
```

## Stubs
- `trochilidae/compat.h`: Standalone stubs for `emalloc`/`erealloc`/`efree`/`estrdup` and minimal Zend types — used by `tr_internal_test.c` and `unit_test.c` when compiled with `-DTROCHILIDAE_STANDALONE`.
14 changes: 7 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ RUN apk add --no-cache unzip git && \
# trochilidae
RUN apk add --no-cache build-base autoconf unzip git curl
ADD . /app
# php /app/test.php && \
RUN phpize && ./configure --enable-trochilidae && make clean && make install && docker-php-ext-enable trochilidae && \
php -m && \
php -r "echo 'Test TR at ',date('c'), PHP_EOL;" && \
echo "OK - exit" && \
composer install --prefer-dist && \
exit 1
RUN phpize && \
./configure --enable-trochilidae && \
make -j$(nproc) && \
make install && \
docker-php-ext-enable trochilidae && \
php -m | grep trochilidae && \
tests/install_test.sh
Comment on lines +125 to +131

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Добавьте запуск полного набора тестов в процесс валидации Docker.

В данный момент проверка в Docker ограничена запуском smoke-теста tests/install_test.sh. Согласно накопленному опыту (learnings), при валидации изменений необходимо также запускать PHP .phpt тесты, сетевые и внутренние C-тесты.

🔧 Предлагаемое исправление
 RUN phpize && \
     ./configure --enable-trochilidae && \
     make -j$(nproc) && \
+    php -d extension=modules/trochilidae.so run-tests.php -d extension=modules/trochilidae.so tests/ && \
+    gcc -I. -Itests/stubs -DHAVE_CONFIG_H -g -O0 tests/unit_test.c trochilidae/tr_network.c trochilidae/utils.c -o tests/unit_test -lm && ./tests/unit_test && \
+    gcc -I. -Itests/stubs -DHAVE_CONFIG_H -g -O0 tests/tr_internal_test.c -o tests/tr_internal_test -lm && ./tests/tr_internal_test && \
     make install && \
     docker-php-ext-enable trochilidae && \
     php -m | grep trochilidae && \
     tests/install_test.sh
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 125 - 131, Расширьте финальную проверку после сборки
расширения в цепочке RUN, где сейчас запускается tests/install_test.sh: добавьте
выполнение полного набора PHP .phpt-тестов, сетевых тестов и внутренних
C-тестов, сохранив существующий smoke-тест и сделав так, чтобы любой сбой
останавливал сборку Docker.

Source: Learnings


99 changes: 88 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,96 @@
# PHP Trochilidae extension
A PHP extension for metrics collect
# PHP Trochilidae

[![CodeFactor](https://www.codefactor.io/repository/github/xmlex/php_trochilidae/badge/main)](https://www.codefactor.io/repository/github/xmlex/php_trochilidae/overview/main)
[![Build Status](https://app.travis-ci.com/xMlex/php_trochilidae.svg?branch=main)](https://app.travis-ci.com/xMlex/php_trochilidae)
A PHP extension for collecting and sending application metrics (timers, tags, request data) to a remote collector via UDP.

## Requirements

- PHP 7.4+
- php-dev (for compilation)

## Install

```bash
phpize
./configure --enable-trochilidae
make
make test
make install
```

Enable the extension in `php.ini`:

```ini
extension=trochilidae.so
trochilidae.enabled=1
trochilidae.server_list=collector1.local:30002,collector2.local:30002
```
phpize && ./configure --enable-trochilidae && make && make test
php -d extension=`pwd`/modules/trochilidae.so -f test.php
run /home/mlex/CLionProjects/php_trochilidae/test.php

## Configuration

| INI directive | Type | Default | Description |
|---|---|---|---|
| `trochilidae.enabled` | bool | `1` | Enable or disable the extension |
| `trochilidae.server_list` | string | empty | Comma-separated `host:port` pairs of collector servers |

## How it works

Trochilidae hooks into the PHP request lifecycle:

1. **RINIT** — resets metrics, collects pre-request data (tags, server vars)
2. During the request, you can add tags and use timers via PHP functions
3. **RSHUTDOWN** — automatically serialises and sends all collected metrics to the configured collector(s) via UDP

For CLI scripts, metrics are also sent on shutdown; call `trochilidae_flush()` explicitly if you need to send data before the end of the script.

## Quick start

```php
<?php

// Set tags and hostname
trochilidae_set_tag('env', 'production');
trochilidae_set_hostname('app01');

// Profile a section of code
trochilidae_timer_start('db_query');
// ... do work ...
trochilidae_timer_stop('db_query');

// Get stats for all timers
$info = trochilidae_timer_get_info();
// $info['timers'][0]['startCount'] – start count
// $info['timers'][0]['stopCount'] – stop count
// $info['timers'][0]['totalExecutionTime'] – total time in seconds
// $info['timers'][0]['lastExecutionTime'] – last lap in seconds

//[optional] Send collected metrics (send by default, after execute script/request)
trochilidae_flush();
```

TODO:
- ~~REQUEST_TIME from php~~
- request id
- ~~timers~~
## PHP Functions

| Function | Description |
|---|---|
| `trochilidae_set_tag(string $key, string $value)` | Set a custom tag |
| `trochilidae_set_hostname(string $hostname)` | Override the default hostname |
| `trochilidae_timer_start(string $name)` | Start or resume a named timer |
| `trochilidae_timer_stop(string $name)` | Stop a named timer (no-op if not running) |
| `trochilidae_timer_get_info()` | Get all timer statistics as nested array |
| `trochilidae_flush()` | Send collected metrics to the collector |
| `trochilidae_reset()` | Reset all collected metrics |

## TODO

### 🟢 Protocol (`docs/protocol.md`)

| # | Change | Why |
|---|---|---|---|
| P2 | **tv_sec → 64-bit `long`** | Avoid Y2038 overflow (time_t is 64-bit on modern systems) |

### 🔴 Stability & Scalability (code)

| # | Change | Where | Why |
|---|---|---|---|
| S10 | **IPv6 support (`sockaddr_storage` + `getaddrinfo`)** | `tr_network.h:62` | Currently `sockaddr_in` = IPv4 only |
| S12 | **Fix `tv_usec` calc: `1e6 * 1000` → `1e6`** | `trochilidae.c:502,508` | Double multiplication produces wrong microseconds |

Loading
Loading