# PHP Zero-Code Instrumentation with Uptrace

> Automatically instrument PHP applications without code changes using OpenTelemetry PHP extension. Export traces, metrics, and logs to Uptrace via OTLP.

![undefined](/devicon/php-original.svg)This guide explains how to automatically instrument PHP applications without modifying code using the OpenTelemetry PHP extension. The extension uses Zend hooks to automatically detect and instrument popular PHP frameworks and libraries at runtime, exporting telemetry data to Uptrace using OTLP.

<alert type="info">

**Note:** The examples below use Uptrace as the OTLP backend, but you can use any OpenTelemetry-compatible backend (Jaeger, Grafana Tempo, Prometheus, etc.) by changing the `OTEL_EXPORTER_OTLP_ENDPOINT` and removing Uptrace-specific headers.

</alert>

## What is Zero-Code Instrumentation?

Zero-code instrumentation (also called automatic instrumentation) allows you to collect telemetry data from PHP applications without modifying application code. The OpenTelemetry PHP extension uses Zend observer hooks to intercept function calls in popular frameworks and libraries at runtime, automatically generating [distributed traces](/opentelemetry/distributed-tracing), [metrics](/opentelemetry/metrics), and [logs](/opentelemetry/logs).

**How it works:**

1. Install the OpenTelemetry PHP extension (PECL, package manager, or Docker)
2. Enable the extension in php.ini
3. Install framework-specific instrumentation libraries via Composer
4. Configure environment variables
5. Run your PHP application - telemetry data is automatically collected and exported to Uptrace

## Prerequisites

Before starting, ensure you have:

- PHP 8.0 or higher (PHP 8.1+ recommended for best performance)
- Composer for dependency management
- An existing PHP application ([Laravel](/guides/opentelemetry-laravel), [Symfony](/guides/opentelemetry-symfony), etc.)
- An Uptrace account with a [DSN](/get#dsn)

## Quick Start Guide

Follow these steps to get your first auto-instrumented trace running in 5 minutes.

### Step 1: Create an Uptrace Project

[Create](/get) an Uptrace project to obtain a [DSN](/get#dsn) (Data Source Name), for example, `https://<secret>@api.uptrace.dev?grpc=4317`.

### Step 2: Install OpenTelemetry PHP Extension

Install the PHP extension using one of the following methods:

<code-group>

```shell [PECL (Recommended)]
# Install build dependencies (if not already installed)
sudo apt-get install gcc make autoconf

# Install via PECL
pecl install opentelemetry

# Enable extension
echo "extension=opentelemetry.so" | sudo tee -a /etc/php/8.2/cli/php.ini
echo "extension=opentelemetry.so" | sudo tee -a /etc/php/8.2/fpm/php.ini
```

```shell [Ubuntu/Debian]
# Install from package repository
sudo apt-get install php-opentelemetry

# Verify installation
php --ri opentelemetry
```

```shell [Alpine Linux (Docker)]
# Add testing repository
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories

# Install extension
apk add php82-pecl-opentelemetry@testing

# Verify
php --ri opentelemetry
```

```dockerfile [Docker (php-extension-installer)]
FROM php:8.2-fpm

# Install OpenTelemetry extension
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions opentelemetry

# Verify installation
RUN php --ri opentelemetry
```

</code-group>

Verify the extension is loaded:

```shell
php --ri opentelemetry
# Should display: opentelemetry support => enabled
```

### Step 3: Install Instrumentation Libraries

Install OpenTelemetry SDK and framework-specific instrumentation packages:

```shell
# Core SDK and OTLP exporter
composer require \
    open-telemetry/sdk \
    open-telemetry/exporter-otlp

# Auto-instrumentation libraries (install only what you need)
composer require \
    open-telemetry/opentelemetry-auto-laravel \
    open-telemetry/opentelemetry-auto-symfony \
    open-telemetry/opentelemetry-auto-slim \
    open-telemetry/opentelemetry-auto-psr15 \
    open-telemetry/opentelemetry-auto-psr18
```

Or for specific frameworks:

<code-group>

```shell [Laravel]
composer require \
    open-telemetry/sdk \
    open-telemetry/exporter-otlp \
    open-telemetry/opentelemetry-auto-laravel
```

```shell [Symfony]
composer require \
    open-telemetry/sdk \
    open-telemetry/exporter-otlp \
    open-telemetry/opentelemetry-auto-symfony
```

```shell [Slim]
composer require \
    open-telemetry/sdk \
    open-telemetry/exporter-otlp \
    open-telemetry/opentelemetry-auto-slim
```

</code-group>

### Step 4: Configure Environment Variables

Configure the agent to export data to Uptrace. Replace `<FIXME>` with your actual Uptrace DSN, and `myservice` with a name that identifies your application:

```shell
export OTEL_PHP_AUTOLOAD_ENABLED=true
export OTEL_SERVICE_NAME=myservice
export OTEL_SERVICE_VERSION=1.0.0
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<FIXME>
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_PROPAGATORS=tracecontext,baggage
```

<alert type="info">

**Note:** OpenTelemetry PHP typically uses HTTP/Protobuf protocol. Uptrace Cloud accepts HTTP on port 443 (implicit with HTTPS) and gRPC on port 4317. For gRPC, use `https://api.uptrace.dev:4317` and set `OTEL_EXPORTER_OTLP_PROTOCOL=grpc`.

</alert>

Alternatively, add to your php.ini file:

```ini
[opentelemetry]
extension=opentelemetry.so
otel.php.autoload_enabled=true
otel.service.name=myservice
otel.traces.exporter=otlp
otel.exporter.otlp.endpoint=https://api.uptrace.dev
otel.exporter.otlp.headers=uptrace-dsn=<FIXME>
```

### Step 5: Run Your Application

Run your PHP application as usual. The extension will automatically instrument it:

<code-group>

```shell [PHP Built-in Server]
php -S localhost:8000
```

```shell [Laravel]
php artisan serve
```

```shell [Symfony]
symfony server:start
```

```shell [PHP-FPM + Nginx]
# Ensure environment variables are passed to PHP-FPM
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx
```

</code-group>

### Step 6: View Your Trace

Navigate to the Uptrace UI to view your traces:

![Basic trace](/get/basic-trace.png)

## Auto-Instrumented Libraries

The OpenTelemetry PHP extension automatically instruments popular PHP frameworks and libraries when corresponding instrumentation packages are installed:

### Web Frameworks

- [Laravel](/guides/opentelemetry-laravel) - automatic request/response tracing, Eloquent queries
- [Symfony](/guides/opentelemetry-symfony) - HTTP kernel, controllers, Doctrine ORM
- [Slim](/guides/opentelemetry-slim) - routing and middleware
- PSR-15 (HTTP Server Request Handlers) - middleware and request handlers

### HTTP Clients

- PSR-18 (HTTP Client) - automatic tracing of HTTP requests
- Guzzle - HTTP client operations
- cURL - native PHP HTTP requests

### Database Clients

- PDO (PHP Data Objects) - SQL queries and connections
- Doctrine DBAL/ORM - database operations
- Eloquent ORM ([Laravel](/guides/opentelemetry-laravel)) - query builder and models
- mysqli - MySQL native driver

### Other Libraries

- PSR-3 (Logger Interface) - log correlation with traces
- WordPress (community instrumentation available)

For the complete list of available instrumentation libraries, see [OpenTelemetry PHP Instrumentation on Packagist](https://packagist.org/search/?query=open-telemetry&tags=instrumentation).

## Configuration Options

### Environment Variables

Common configuration options for zero-code instrumentation:

```shell
# Service identification
export OTEL_SERVICE_NAME=my-php-app
export OTEL_SERVICE_VERSION=1.0.0
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.namespace=backend

# Enable auto-instrumentation
export OTEL_PHP_AUTOLOAD_ENABLED=true

# Exporter configuration
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<your_dsn>
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

# Sampling configuration
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1  # Sample 10% of traces

# Propagators
export OTEL_PROPAGATORS=tracecontext,baggage

# Logging
export OTEL_LOG_LEVEL=info
```

For the full list of configuration options, see the [OpenTelemetry PHP Configuration](https://opentelemetry.io/docs/languages/php/configuration/) documentation.

### php.ini Configuration

You can also configure options in php.ini:

```ini
[opentelemetry]
extension=opentelemetry.so

; Extension validation
otel.php.validate_hook_functions=On
otel.php.conflicts=none

; Auto-instrumentation
otel.php.autoload_enabled=On

; Service configuration
otel.service.name=my-php-app
otel.service.version=1.0.0

; Exporter configuration
otel.traces.exporter=otlp
otel.exporter.otlp.endpoint=https://api.uptrace.dev
otel.exporter.otlp.headers=uptrace-dsn=<your_dsn>
otel.exporter.otlp.protocol=http/protobuf

; Sampling
otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=1.0
```

### Disabling Specific Instrumentations

Disable instrumentation for specific libraries by not installing their Composer packages. The extension only activates instrumentations for installed packages.

To disable after installation, remove the package:

```shell
composer remove open-telemetry/opentelemetry-auto-laravel
```

## Production Deployment

### Docker Example

```dockerfile
FROM php:8.2-fpm

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    && rm -rf /var/lib/apt/lists/*

# Install OpenTelemetry extension
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions opentelemetry zip

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Copy application files
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY . .

# Configure OpenTelemetry via php.ini
RUN echo "extension=opentelemetry.so" >> /usr/local/etc/php/conf.d/opentelemetry.ini && \
    echo "otel.php.autoload_enabled=On" >> /usr/local/etc/php/conf.d/opentelemetry.ini

# Set environment variables
ENV OTEL_SERVICE_NAME=my-php-app
ENV OTEL_TRACES_EXPORTER=otlp
ENV OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev

EXPOSE 9000
CMD ["php-fpm"]
```

### Docker Compose Example

```yaml
version: '3.8'

services:
  app:
    build: .
    volumes:
      - ./:/app
    environment:
      - OTEL_SERVICE_NAME=laravel-app
      - OTEL_SERVICE_VERSION=1.0.0
      - OTEL_PHP_AUTOLOAD_ENABLED=true
      - OTEL_TRACES_EXPORTER=otlp
      - OTEL_METRICS_EXPORTER=otlp
      - OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev
      - OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=${UPTRACE_DSN}
      - OTEL_TRACES_SAMPLER=parentbased_traceidratio
      - OTEL_TRACES_SAMPLER_ARG=0.1

  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./:/app
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
```

### PHP-FPM Configuration

For PHP-FPM deployments, pass environment variables via the pool configuration:

```ini
; /etc/php/8.2/fpm/pool.d/www.conf

[www]
; ... existing configuration ...

; OpenTelemetry environment variables
env[OTEL_SERVICE_NAME] = my-php-app
env[OTEL_PHP_AUTOLOAD_ENABLED] = true
env[OTEL_TRACES_EXPORTER] = otlp
env[OTEL_EXPORTER_OTLP_ENDPOINT] = https://api.uptrace.dev
env[OTEL_EXPORTER_OTLP_HEADERS] = uptrace-dsn=<your_dsn>
```

Restart PHP-FPM:

```shell
sudo systemctl restart php8.2-fpm
```

## Kubernetes Deployment

For Kubernetes, include the extension in your container image and configure via environment variables:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: my-php-app:latest
        env:
        - name: OTEL_SERVICE_NAME
          value: "php-app"
        - name: OTEL_SERVICE_VERSION
          value: "1.0.0"
        - name: OTEL_PHP_AUTOLOAD_ENABLED
          value: "true"
        - name: OTEL_TRACES_EXPORTER
          value: "otlp"
        - name: OTEL_EXPORTER_OTLP_ENDPOINT
          value: "https://api.uptrace.dev"
        - name: OTEL_EXPORTER_OTLP_HEADERS
          valueFrom:
            secretKeyRef:
              name: uptrace-secrets
              key: dsn
```

For more details, see the [Kubernetes monitoring guide](/get/kubernetes).

## Troubleshooting

### Issue: Extension Not Loading

**Symptom:** No traces appear, `php --ri opentelemetry` shows nothing.

**Solution:**

1. Verify extension is installed:

```shell
php --ri opentelemetry
# Should show: opentelemetry support => enabled
```

1. Check php.ini includes the extension:

```shell
php --ini
grep -r "opentelemetry" /etc/php/*/
```

1. Ensure `extension=opentelemetry.so` is in the correct ini file:

```shell
echo "extension=opentelemetry.so" | sudo tee -a $(php --ini | grep "Loaded Configuration" | awk '{print $4}')
```

### Issue: Auto-instrumentation Not Working

**Symptom:** Extension loaded but no traces generated.

**Solution:**

1. Enable auto-instrumentation:

```shell
export OTEL_PHP_AUTOLOAD_ENABLED=true
```

1. Verify instrumentation packages are installed:

```shell
composer show | grep opentelemetry-auto
```

1. Check that Composer autoloader is included in your application:

```php
// Ensure this is at the top of your entry point
require __DIR__ . '/vendor/autoload.php';
```

### Issue: Missing Framework Traces

**Symptom:** HTTP requests traced but framework-specific operations missing.

**Solution:**

Install the framework-specific instrumentation package:

```shell
# Laravel
composer require open-telemetry/opentelemetry-auto-laravel

# Symfony
composer require open-telemetry/opentelemetry-auto-symfony

# Slim
composer require open-telemetry/opentelemetry-auto-slim
```

### Issue: PHP-FPM Not Sending Traces

**Symptom:** CLI works but PHP-FPM requests don't generate traces.

**Solution:**

PHP-FPM doesn't inherit shell environment variables. Configure in pool config:

```ini
; /etc/php/8.2/fpm/pool.d/www.conf
env[OTEL_PHP_AUTOLOAD_ENABLED] = true
env[OTEL_SERVICE_NAME] = my-app
env[OTEL_TRACES_EXPORTER] = otlp
env[OTEL_EXPORTER_OTLP_ENDPOINT] = https://api.uptrace.dev
env[OTEL_EXPORTER_OTLP_HEADERS] = uptrace-dsn=<your_dsn>
```

Restart PHP-FPM after changes:

```shell
sudo systemctl restart php8.2-fpm
```

### Issue: High Performance Overhead

**Symptom:** Response times increased after enabling instrumentation.

**Solution:**

1. Enable sampling:

```shell
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1  # Sample only 10%
```

1. Use batch span processor (configured automatically, but can be tuned):

```shell
export OTEL_BSP_SCHEDULE_DELAY=5000
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512
```

## Limitations of Zero-Code Instrumentation

While zero-code instrumentation provides good observability, it has some limitations:

### Generic Span Names

Automatic instrumentation generates generic span names:

- ❌ Automatic: `GET /api/orders`
- ✅ Manual: `ProcessHighPriorityOrders`

### No Business Context

Zero-code instrumentation can't capture domain-specific information:

```php
// Automatically traced (HTTP request)
Route::get('/orders/{id}', function ($id) {
    return Order::find($id);
});

// NOT traced (business logic details)
function processOrder($order, $user) {
    // Business rules invisible without manual instrumentation
    if ($user->isPremium()) {
        applyDiscounts($order);
    }
    return $order;
}
```

To add business context, combine zero-code with tracing:

```php
use OpenTelemetry\API\Globals;

function processOrder($order, $user) {
    $tracer = Globals::tracerProvider()->getTracer('my-app');
    $span = $tracer->spanBuilder('process_order')
        ->setAttribute('order.id', $order->id)
        ->setAttribute('user.id', $user->id)
        ->setAttribute('user.tier', $user->tier)
        ->startSpan();

    $scope = $span->activate();

    try {
        $result = applyBusinessRules($order, $user);
        $span->setAttribute('order.total', $result->total);
        return $result;
    } finally {
        $scope->detach();
        $span->end();
    }
}
```

### Framework Coverage Only

Only instrumented frameworks generate spans. Custom code and proprietary frameworks remain invisible without manual instrumentation.

### Performance Overhead

Instrumenting all requests introduces overhead:

- Typical overhead: 2-5% CPU increase
- Memory overhead: 10-30MB additional memory
- Startup time: Minimal (extension loaded once per process)

For performance-critical applications, enable [sampling](/get/opentelemetry-php/sampling) to reduce overhead.

## Zero-Code vs Manual Instrumentation

<table>
<thead>
  <tr>
    <th>
      Aspect
    </th>
    
    <th>
      Zero-Code
    </th>
    
    <th>
      Manual
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        Setup Time
      </strong>
    </td>
    
    <td>
      10-15 minutes
    </td>
    
    <td>
      Hours to days
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Code Changes
      </strong>
    </td>
    
    <td>
      None
    </td>
    
    <td>
      Extensive
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Coverage
      </strong>
    </td>
    
    <td>
      Framework/library operations only
    </td>
    
    <td>
      Full application including business logic
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Customization
      </strong>
    </td>
    
    <td>
      Limited to environment variables
    </td>
    
    <td>
      Complete control over spans and attributes
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Maintenance
      </strong>
    </td>
    
    <td>
      Update packages only
    </td>
    
    <td>
      Code changes for new instrumentation
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Performance
      </strong>
    </td>
    
    <td>
      Moderate (all requests instrumented)
    </td>
    
    <td>
      Lower (instrument only critical paths)
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Best For
      </strong>
    </td>
    
    <td>
      Quick start, legacy apps, proof-of-concept
    </td>
    
    <td>
      Custom business metrics, fine-grained control
    </td>
  </tr>
</tbody>
</table>

**Recommendation:** Start with zero-code instrumentation for immediate visibility, then add tracing incrementally for business-critical operations.

## Next Steps

- Learn about [OpenTelemetry PHP Tracing API](/get/opentelemetry-php/tracing) to add custom spans
- Configure [OpenTelemetry Sampling](/get/opentelemetry-php/sampling) for production environments
- Explore [OpenTelemetry Metrics](/get/opentelemetry-php/metrics) to track business KPIs
- Review [Laravel monitoring guide](/guides/opentelemetry-laravel) for comprehensive setup
- Instrument PHP-FPM using the [OpenTelemetry PHP-FPM guide](/guides/opentelemetry-php-fpm) for full request visibility
- Compare [Symfony instrumentation](/guides/opentelemetry-symfony) for Symfony applications

## See Also

- [OpenTelemetry PHP Official Documentation](https://opentelemetry.io/docs/zero-code/php/)
- [OpenTelemetry PHP Instrumentation Libraries](https://packagist.org/search/?query=open-telemetry&tags=instrumentation)
- [GitHub Repository](https://github.com/open-telemetry/opentelemetry-php-instrumentation)
- [PHP Extension Installation Guide](https://opentelemetry.io/docs/languages/php/getting-started/)
