# PHP

> Laravel, Symfony and PHP-FPM with the OpenTelemetry extension

PHP auto-instrumentation needs a PECL extension that hooks function calls, plus the
Composer packages for the SDK. With both present, Laravel and Symfony requests, PDO
queries and Guzzle calls are traced without code changes.

## Install

```bash
# 1. The hook extension
pecl install opentelemetry
echo "extension=opentelemetry.so" >> "$(php -i | grep '^Loaded Configuration File' | cut -d'>' -f2 | xargs)"

# 2. The SDK and exporter
composer require \
  open-telemetry/sdk \
  open-telemetry/exporter-otlp \
  open-telemetry/opentelemetry-auto-laravel
```

Swap the last package for `opentelemetry-auto-symfony`, `-auto-slim` or `-auto-psr18`
depending on your framework. Verify the extension loaded with `php -m | grep opentelemetry`.

## Configure

```bash
export OTEL_PHP_AUTOLOAD_ENABLED=true
export OTEL_SERVICE_NAME="my-php-service"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingress.rocketlog.io"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer rg_live_xxxxxx"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_TRACES_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
```

> **Warning:** `OTEL_PHP_AUTOLOAD_ENABLED=true` is the switch that actually turns auto-instrumentation
  on. Everything else can be correct and nothing will be exported without it.

## Under PHP-FPM

Shell exports do not reach FPM workers. Set them in the pool config instead:

```ini
; /usr/local/etc/php-fpm.d/www.conf
env[OTEL_PHP_AUTOLOAD_ENABLED] = true
env[OTEL_SERVICE_NAME] = my-php-service
env[OTEL_EXPORTER_OTLP_ENDPOINT] = https://ingress.rocketlog.io
env[OTEL_EXPORTER_OTLP_PROTOCOL] = http/protobuf
env[OTEL_EXPORTER_OTLP_HEADERS] = Authorization=Bearer rg_live_xxxxxx
```

## Custom spans

```php
use OpenTelemetry\API\Globals;

$tracer = Globals::tracerProvider()->getTracer('payments');

$span = $tracer->spanBuilder('settle-transfer')->startSpan();
$scope = $span->activate();

try {
    $span->setAttribute('transfer.id', $transferId);
    settle($transferId);
} catch (\Throwable $e) {
    $span->recordException($e);
    throw $e;
} finally {
    $scope->detach();
    $span->end();
}
```
