This page describes the PHP Agent API and provides example use cases for the API.

About the PHP Agent API

The PHP Agent API enables you to: 

  • Define custom business transactions programmatically
  • Provide correlation headers for entry points not supported by default detection
  • Create custom exit calls to discover backends that are not automatically detected by the PHP Agent

Include the AppDynamics API Header

You should include the appdynamics_api_header.php file in your application to ensure that it works correctly if the agent is uninstalled or temporarily disabled. This file contains empty API functions that will prevent the application from throwing errors if the agent is not present.

The appdynamics_api_header.php file is located in the PHP Agent package in the same directory as the install.sh script.

To include the header file:

  1. Copy appdynamics_api_header.php to where you keep the header files for the monitored application.
  2. Make sure that appdynamics_api_header.php is in your include path.
  3. Then add the following to your script:
 require 'appdynamics_api_header.php';

Backend Detection with the MySQLi Driver

As noted on PHP Supported Environments, the PHP Agent does not work with PHP 5.2 applications that use the new keyword to instantiate a database backend with the MySQLi database driver. For example, AppDynamics does not detect the MySQLi backend created as follows in a PHP 5.2 application:

$db = new mysqli("localhost", "user", "password", "database");
CODE

You can work around this by using mysqli_connect() instead:

$db = mysqli_connect("localhost", "user", "password", "database");
CODE

Parts of a Script are Business Transactions

If you have a long PHP script application that performs a number of discrete tasks, but you want the agent to detect only one or more of them as business transactions, enclose the code for each of those tasks within appdynamics_start_transaction() and appdynamics_end_transaction() calls. The agent detects those blocks as separate business transactions. Otherwise, the agent detects the entire script as a single business transaction.

Handling Business Transaction Code Executing in a Loop

For a script-based CLI application that executes in a loop, perhaps fetching items from a database or remote service, you may want the agent to detect every iteration of the loop as a separate business transaction. In this case, enclose the code inside the loop within appdynamics_start_transaction() and appdynamics_end_transaction() calls.

If you do not do this, the agent will aggregate each iteration through the loop into a single business transaction.

In the following example, the agent detects a business transaction named getItem for every iteration.

while (true){
    appdynamics_start_transaction("getItem", AD_CLI);
    //your code goes here
    . . .
    appdynamics_end_transaction();
}
CODE

Correlating with an Upstream Service

If you have a distributed business transaction in which a tier needs to correlate with an upstream service that is not an entry point supported by the PHP Agent, you can maintain transaction correlation using appdynamics_continue_transaction() in the downstream tier, passing it the correlation header from the service.

Transaction Correlation

You need to extract the correlation header from the service as shown in the following sample. The sample function extracts the correlation header from each message in an AMQP message queue and passes it to appdynamics_continue_transaction().

After processing the message, it calls appdynamics_end_transaction(), which ends the continuation of the transaction on the calling tier. The appdynamics_end_transaction() call does not end the entire distributed transaction in the case where that tier makes a distributed call to another downstream tier.

function amqp_receive($exchangeName, $routingKey, $queueName) {
    $amqpConnection = amqp_connection();
    $channel = new AMQPChannel($amqpConnection);
    $queue = new AMQPQueue($channel);
    $queue->setName($queueName);
    $queue->bind($exchangeName, $routingKey);
    while($message = $queue->get()) {
        // Extracting the correlation header.
        echo("Message #".$message->getDeliveryTag()." '".$message->getBody()."'");
        echo("Correlation header: " . $message->getHeader("singularityheader"));
        // Passing correlation header to API.
        appdynamics_continue_transaction($message->getHeader("singularityheader"));
        doStuff($message);
        // End transaction.
        appdynamics_end_transaction();
    }
    if(!$amqpConnection->disconnect()) {
        throw new Exception("Could not disconnect !");
    }
}
CODE

 If the service is not a supported entry point and you do not do this, the tier will not be correlated with the upstream transaction.

Make Socket-based HTTP Calls Example

While the API does not include built-in calls for socket-based HTTP call, you can implement monitoring of socket-based HTTP exit calls yourself as shown in the following example: 

<?php

function doSocketHTTPCall($url, $corrHeader = null)
{
    $parts = parse_url($url);
    $fs = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $error);
    if (!$fs)
        return null;
    $send = "GET {$parts['path']} HTTP/1.1\r\n" .
        "Host: {$parts['host']}\r\n" .
        "Connection: Close\r\n";
    if ($corrHeader)
        $send .= "singularityheader: $corrHeader\r\n";
    $send .= "\r\n";
    fwrite($fs, $send);
    $data = stream_get_contents($fs);
    fclose($fs);
    return $data;
}

$url = 'http://httpstat.us/200';
$parts = parse_url($url);

$exitCall = appdynamics_begin_exit_call(
                AD_EXIT_HTTP,
                'HTTP Status Service',
                array('HOST' => $parts['host'],
                      'PORT' => (string)$parts['port'])
            );

doSocketHTTPCall($url);

appdynamics_end_exit_call($exitCall);
?>

CODE

Inject a Correlation Header into an HTTP Payload Example

The next example injects a correlation header into the socket-based HTTP payload.

<?php

$url = 'http://myhost.mydomain/continue.php';
$parts = parse_url($url);

$exitCall = appdynamics_begin_exit_call(
                AD_EXIT_HTTP,
                'HTTP Status Service',
                array('HOST' => $parts['host'],
                      'PORT' => (string)$parts['port'])
            );

$corrHeader = $exitCall->getCorrelationHeader();

doSocketHTTPCall($url, $corrHeader);

appdynamics_end_exit_call($exitCall);
?>

CODE

The next example shows how to use a non-exclusive flag to start an exit call that may be wrapping other exit calls. The outer socket-HTTP call is started, then the file_get_contents() call is processed by the agent normally, and finally, the outer call is finished. We also pass the exception object to report any errors. The end result is that both backends are displayed on the flowmap.

<?php

class SocketHTTPException extends Exception
{
}

$url = 'http://httpstat.us/200';
$javaTierURL = 'http://myhost.mydomain/process.jsp';
$parts = parse_url($url);

$exitCall = appdynamics_begin_exit_call(
                AD_EXIT_HTTP,
                'HTTP Status Service',
                array('HOST' => $parts['host'],
                      'PORT' => (string)$parts['port']),
                false
            );

$contents = file_get_contents($javaTierURL);

if (doSocketHTTPCall($url) == null) {
    $error = new SocketHTTPException("something bad happened");
}

appdynamics_end_exit_call($exitCall, $error);

?>
CODE