# PHP zero-code instrumentation

## Requirements

Automatic instrumentation with PHP requires:

- PHP 8.0 or higher
- [OpenTelemetry PHP extension](https://github.com/open-telemetry/opentelemetry-php-instrumentation)
- [Composer autoloading](https://getcomposer.org/doc/01-basic-usage.md#autoloading)
- [OpenTelemetry SDK](https://packagist.org/packages/open-telemetry/sdk)
- One or more
  [instrumentation libraries](/ecosystem/registry/?component=instrumentation&language=php)
- [Configuration](#configuration)

## Install the OpenTelemetry extension

> [!IMPORTANT]
>
> Installing the OpenTelemetry extension by itself does not generate traces.

The extension can be installed via pecl,
[pickle](https://github.com/FriendsOfPHP/pickle),
[PIE](https://github.com/php/pie) or
[php-extension-installer](https://github.com/mlocati/docker-php-extension-installer)
(docker specific). There are also packaged versions of the extension available
for some Linux package managers.

### Linux packages

RPM and APK packages are provided by the following:

- [Remi repository](https://blog.remirepo.net/pages/PECL-extensions-RPM-status) -
  RPM
- [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=*pecl-opentelemetry) -
  APK (currently in the
  [_testing_ branch](https://wiki.alpinelinux.org/wiki/Repositories#Testing))

   <ul class="nav nav-tabs" id="tabs-0" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab"
          data-td-tp-persist="rpm" aria-controls="tabs-00-00" aria-selected="true">
        RPM
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab"
          data-td-tp-persist="apk" aria-controls="tabs-00-01" aria-selected="false">
        APK
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-0-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1">#this example is for CentOS 7. The PHP version can be changed by</span>
</span></span><span class="line"><span class="cl"><span class="c1">#enabling remi-&lt;version&gt;, eg &#34;yum config-manager --enable remi-php83&#34;</span>
</span></span><span class="line"><span class="cl">yum update -y
</span></span><span class="line"><span class="cl">yum install -y epel-release yum-utils
</span></span><span class="line"><span class="cl">yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
</span></span><span class="line"><span class="cl">yum-config-manager --enable remi-php81
</span></span><span class="line"><span class="cl">yum install -y php php-pecl-opentelemetry
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">php --ri opentelemetry
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1">#At the time of writing, PHP 8.1 was the default PHP version. You may need to</span>
</span></span><span class="line"><span class="cl"><span class="c1">#change &#34;php81&#34; if the default changes. You can alternatively choose a PHP</span>
</span></span><span class="line"><span class="cl"><span class="c1">#version with &#34;apk add php&lt;version&gt;&#34;, eg &#34;apk add php83&#34;.</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing&#34;</span> &gt;&gt; /etc/apk/repositories
</span></span><span class="line"><span class="cl">apk add php php81-pecl-opentelemetry@testing
</span></span><span class="line"><span class="cl">php --ri opentelemetry
</span></span></code></pre></div>
    </div>
</div>


### PECL

1. Setup development environment. Installing from source requires proper
   development environment and some dependencies:

      <ul class="nav nav-tabs" id="tabs-1" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-01-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-01-00" role="tab"
          data-td-tp-persist="linux (apt)" aria-controls="tabs-01-00" aria-selected="true">
        Linux (apt)
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-01-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-01-01" role="tab"
          data-td-tp-persist="macos (homebrew)" aria-controls="tabs-01-01" aria-selected="false">
        macOS (homebrew)
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-1-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-01-00" role="tabpanel" aria-labelled-by="tabs-01-00-tab" tabindex="1">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo apt-get install gcc make autoconf
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-01-01" role="tabpanel" aria-labelled-by="tabs-01-01-tab" tabindex="1">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">brew install gcc make autoconf
</span></span></code></pre></div>
    </div>
</div>


2. Build/install the extension. With your environment set up you can install the
   extension:

       <ul class="nav nav-tabs" id="tabs-2" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-02-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-02-00" role="tab"
          data-td-tp-persist="pecl" aria-controls="tabs-02-00" aria-selected="true">
        pecl
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-02-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-02-01" role="tab"
          data-td-tp-persist="pickle" aria-controls="tabs-02-01" aria-selected="false">
        pickle
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-02-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-02-02" role="tab"
          data-td-tp-persist="php-extension-installer (docker)" aria-controls="tabs-02-02" aria-selected="false">
        php-extension-installer (docker)
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-2-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-02-00" role="tabpanel" aria-labelled-by="tabs-02-00-tab" tabindex="2">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pecl install opentelemetry
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-02-01" role="tabpanel" aria-labelled-by="tabs-02-01-tab" tabindex="2">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">php pickle.phar install opentelemetry
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-02-02" role="tabpanel" aria-labelled-by="tabs-02-02-tab" tabindex="2">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">install-php-extensions opentelemetry
</span></span></code></pre></div>
    </div>
</div>


3. Add the extension to your `php.ini` file:

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

4. Verify that the extension is installed and enabled:

   ```sh
   php -m | grep opentelemetry
   ```

## Install SDK and instrumentation libraries

Now that the extension is installed, install the OpenTelemetry SDK and one or
more instrumentation libraries.

Automatic instrumentation is available for a number of commonly used PHP
libraries. For the full list, see
[instrumentation libraries on packagist](https://packagist.org/search/?query=open-telemetry&tags=instrumentation).

Let's assume that your application uses Slim Framework and a PSR-18 HTTP client,
and that we will export the traces with the OTLP protocol.

You would then install the SDK, an exporter, and auto-instrumentation packages
for Slim Framework and PSR-18:

```shell
composer require \
    open-telemetry/sdk \
    open-telemetry/exporter-otlp \
    open-telemetry/opentelemetry-auto-slim \
    open-telemetry/opentelemetry-auto-psr18
```

## Configuration

When used in conjunction with the OpenTelemetry SDK, you can use environment
variables or the `php.ini` file to configure auto-instrumentation.

### Environment configuration

```sh
OTEL_PHP_AUTOLOAD_ENABLED=true \
OTEL_SERVICE_NAME=your-service-name \
OTEL_TRACES_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 \
OTEL_PROPAGATORS=baggage,tracecontext \
php myapp.php
```

### php.ini configuration

Append the following to `php.ini`, or another `ini` file that will be processed
by PHP:

```ini
OTEL_PHP_AUTOLOAD_ENABLED="true"
OTEL_SERVICE_NAME=your-service-name
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
OTEL_PROPAGATORS=baggage,tracecontext
```

## Run your application

After all of the above is installed and configured, start your application as
you normally would.

The traces you see exported to the OpenTelemetry Collector depend on the
instrumentation libraries you have installed, and the code path that was taken
inside the application. In the previous example, using Slim Framework and PSR-18
instrumentation libraries, you should expect to see spans such as:

- A root span representing the HTTP transaction
- A span for the action that was executed
- A span for each HTTP transaction that the PSR-18 client sent

Note that the PSR-18 client instrumentation appends
[distributed tracing](/docs/concepts/context-propagation/#propagation) headers
to outgoing HTTP requests.

## How it works

> [!NOTE] Optional
>
> You can skip over this section if you just want to get up and running quickly,
> and there are suitable instrumentation libraries for your application.

The extension enables registering observer functions as PHP code against classes
and methods, and executing those functions before and after the observed method
runs.

If there is not an instrumentation library for your framework or application,
you can write your own. The following example provides some code to be
instrumented, and then illustrates how to use the OpenTelemetry extension to
trace the execution of that code.

```php
<?php

use OpenTelemetry\API\Instrumentation\CachedInstrumentation;
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\StatusCode;
use OpenTelemetry\Context\Context;

require 'vendor/autoload.php';

/* The class to be instrumented */
class DemoClass
{
    public function run(): void
    {
        echo 'Hello, world';
    }
}

/* The auto-instrumentation code */
OpenTelemetry\Instrumentation\hook(
    class: DemoClass::class,
    function: 'run',
    pre: static function (DemoClass $demo, array $params, string $class, string $function, ?string $filename, ?int $lineno) {
        static $instrumentation;
        $instrumentation ??= new CachedInstrumentation('example');
        $span = $instrumentation->tracer()->spanBuilder('democlass-run')->startSpan();
        Context::storage()->attach($span->storeInContext(Context::getCurrent()));
    },
    post: static function (DemoClass $demo, array $params, $returnValue, ?Throwable $exception) {
        $scope = Context::storage()->scope();
        $scope->detach();
        $span = Span::fromContext($scope->context());
        if ($exception) {
            $span->recordException($exception);
            $span->setStatus(StatusCode::STATUS_ERROR);
        }
        $span->end();
    }
);

/* Run the instrumented code, which will generate a trace */
$demo = new DemoClass();
$demo->run();
```

The previous example defines `DemoClass`, then registers `pre` and `post` hook
functions on its `run` method. The hook functions run before and after each
execution of the `DemoClass::run()` method. The `pre` function starts and
activates a span, while the `post` function ends it.

If `DemoClass::run()` throws an exception, the `post` function records it
without affecting exception propagation.

## Next steps

After you have automatic instrumentation configured for your app or service, you
might want to add [manual instrumentation](/docs/languages/php/instrumentation)
to collect custom telemetry data.

For more examples, see
[opentelemetry-php-contrib/examples](https://github.com/open-telemetry/opentelemetry-php-contrib/tree/main/examples).
