Getting Started with PHP and InfluxDB

Navigate to:

This article was written by Cameron Pavey, a full-stack dev living and working in Melbourne. Scroll below for his picture and bio. 

As a developer, it is likely that you will eventually run into a situation where a traditional relational database’s document stores don’t quite cut it. If you need to store points of data over time, you’ll likely need a time series database.

Time series databases are great for storing system monitoring data, analytical reporting data, and data coming from sensors such as heart rate sensors and temperature sensors. Any time you want to see the trend of data over time, time series databases have you covered.

InfluxDB is a robust, open source, time series platform with various client libraries for numerous languages, making it easy to bring features to your application driven by time series data.

In this guide, you will learn how to set up InfluxDB with a PHP application, the fundamentals of connecting to the database, writing data, reading data, and what you can do with it once it is all hooked up.

InfluxDB is a robust, open source, time series platform with various client libraries for numerous languages, making it easy to bring features to your application driven by time series data.

InfluxDB client library

For this guide, you will use the official PHP library to connect to your InfluxDB instance. This library is for InfluxDB 2.x and 1.8+. For versions 1.7 and earlier, you will need to use the older InfluxDB client library; however, it is not compatible with this guide.

To get started, you will need to satisfy a few prerequisites. Naturally, you will need to have a running InfluxDB instance to point your application at. This guide was written using InfluxDB v2.0 and PHP 8 installed via Homebrew on a macOS system.

Still, the steps should also work for other operating systems and installation sources such as Docker. You can refer to the official documentation for InfluxDB and PHP for the right installation option for your setup.

If you haven’t already created a directory to house your code for this guide, go ahead and do that now. Once you have a directory, you can use composer to install the InfluxDB client by executing the following command from your terminal:

$ composer require influxdata/influxdb-client-php

You will also need to create a bucket in InfluxDB to store your data. This guide will assume your org and bucket are both named “influx”, but feel free to call them whatever you want.

If you have not used InfluxDB before, once it is installed, navigate to [localhost:8086], and you will be prompted to set up your initial user details. You can then use these details for the remainder of the guide.

Making a connection

To use your InfluxDB instance in your PHP application, you need to establish a connection using the client library you downloaded with Composer.

First, you need to create an instance of the client and provide it with the details for your instance. To do this, you can create a variable like so:

# First, let's make use of Composer so that we can access the Library
require __DIR__ . '/vendor/autoload.php';

# You can generate a Token from the "Tokens Tab" in the UI
$token = '...';
$org = 'influx';
$bucket = 'influx';

# Next, we will instantiate the client and establish a connection
$client = new InfluxDB2\Client([
	"url" => "http://localhost:8086", // url and port of your instance
	"token" => $token,
	"bucket" => $bucket,
	"org" => $org,
	"precision" => InfluxDB2\Model\WritePrecision::NS,
]);

$writeApi = $client->createWriteApi();

To check the status of the InfluxDB server to ensure everything is working as anticipated, you can use $client->health();. This function will perform an influx ping and return an object describing the server’s state and whether or not it is ready to receive queries.

For this guide, you will see the various options available to you when it comes to reading and writing data to InfluxDB. To help frame these examples, see the following JSON object. This is a rough estimation of the data point we want to add to our DB.

{
  "measurement": "temp_c",
  "tags": [
	"location": "Melbourne"
  ],
  "fields": [
	"degrees": 14,
	"humidity": 0.57
  ]
}

Inserting data

Once you have your instance set up and the initialization code in your PHP application, you can start inserting data. As a time series database, InfluxDB doesn’t behave quite the same way as relational databases. Rather than conforming to a strictly pre-defined schema, you can insert data more fluidly. Data is separated into buckets. Within a given bucket, you have measurements that act as data points, and for a given measurement, you can have field values and tags.

It is good to note that measurements and tags are indexed while field values are not, so it is a good idea to structure your data in such a way that your commonly queried metadata is encoded in tags. There are many nuances around designing an effective data layout, so it is recommended you read the official documentation on the topic from InfluxData before designing your schema.

For now though, we are not really concerned with these finer details. Our purpose is to see how we can bring the power of InfluxDB to our PHP application. As such, we will stick with a simple schema.

When it comes to inserting data via the PHP client, there are three main ways to do this.

Use the InfluxDB Line Protocol

The line protocol is a standard data format represented by a string. Line protocol is made up of a few discrete elements which you can use to provide the data you wish to insert.

measurementName,tagKey=tagValue fieldKey="fieldValue" 1465839830100400200
--------------- --------------- --------------------- -------------------
   	|           	|              	|                	|
  Measurement   	Tag set       	Field set        	Timestamp

Here you can see the elements of the line protocol. The first value is the measurement, followed by a comma-separated list of tag key-value pairs, and then a comma-separated list of field key-value pairs, and then finally, the timestamp for the data point.

$data = "temp_c,location=Melbourne degrees=14,humidity=0.57";

$writeApi->write($data, WritePrecision::S, $bucket, $org);

This approach is the simplest in concept and can be useful when you already have data processed into a line protocol format, such as from another API or a text file.

Use a data point

If you’d rather avoid manually constructing line protocol strings, your next option is to use a data point builder provided by the client library to properly serialize your data in a way native to PHP. This is an easy, declarative way to construct a single data point, and is a good way to insert some data if you have a limited number of tags and fields.

$point = Point::measurement('temp_c')
  ->addTag('location', 'Melbourne')
  ->addField('degrees', 14)
  ->addField('humidity', 0.57)
  ->time(microtime(true));

$writeApi->write($point, WritePrecision::S, $bucket, $org);

Use an array structure

You can also insert data using an array structure. This is similar to the previous option of using a data point but is more conducive to adding multiple tags and fields. It is a nice option to have, especially when paired with PHP’s array functions, which allow you to easily extract and transform data from whatever your source may be and map it into something more suitable for storage in InfluxDB.

$dataArray = ['name' => 'temp_c',
  'tags' => ['location' => 'Melbourne'],
  'fields' => ['degrees' => 14, 'humidity' => 0.57],
  'time' => microtime(true)];

$writeApi->write($dataArray, WritePrecision::S, $bucket, $org);

Querying data

Now that you have some data in InfluxDB, it is time to query it back out. The two main ways of doing this are by using a table structure or a stream. Either way, we must first instantiate a query client.

$queryApi = $client->createQueryApi();

InfluxDB queries are written using the Flux query language. If you are not familiar with it or need a refresher, the official docs are a great place to start.

Table structure

The first way you can query data is as a table structure.

$query = "from(bucket: \"$bucket\")
	|> range(start: -1h)
	|> filter(fn: (r) => r._measurement == \"temp_c\")";

$records = $queryApi->query($query, $org);

This will return an array of InfluxTable objects, representing the queried data’s CSV form.

The queryStream method

Your other option is to use the queryStream method, which will return an object allowing you to access the CSV data using the generator pattern. This is great for cases where memory is a concern due to limited resources, large data sets or just preference.

$query = "from(bucket: \"$bucket\")
	|> range(start: -1h)
	|> filter(fn: (r) => r._measurement == \"temp_c\")";

$parser = $queryApi->queryStream($query, $org);

foreach($parser->each() as $record) {
  ...
}

Putting it all together

Now that you’ve seen how to set up the client library, write data, and then read the data back out, it is time to put all the pieces together. Try them out in a script that writes some data points, reads them back, and performs some logic on them.

<?php
use InfluxDB2\Model\WritePrecision;

require __DIR__ . '/vendor/autoload.php';

# You can generate a Token from the "Tokens Tab" in the UI
$token = '...'; # your token here
$org = 'influx';
$bucket = 'influx';

$client = new InfluxDB2\Client([
	"url" => "http://localhost:8086", // url and port of your instance
	"token" => $token,
]);

// The base data we will be inserting
$rawData = [
	["temp" => 8, 'humidity' => .57],
	["temp" => 8, 'humidity' => .59],
	["temp" => 7, 'humidity' => .60],
	["temp" => 7, 'humidity' => .58],
	["temp" => 7, 'humidity' => .54],
	["temp" => 9, 'humidity' => .53],
	["temp" => 10, 'humidity' => .55],
	["temp" => 10, 'humidity' => .59],
	["temp" => 11, 'humidity' => .60],
];

$writeApi = $client->createWriteApi();
foreach ($rawData as $index => $datum) {
	$dataArray = [
    	'name' => 'temp_c',
    	'tags' => ['location' => 'Melbourne'],
    	'fields' => ['degrees' => $datum['temp'], 'humidity' => $datum['humidity']],
    	'time' => microtime(true) - (7200 * $index), // we will populate data going back by the hour
	];
	$writeApi->write($dataArray, WritePrecision::S, $bucket, $org);
}

$queryApi = $client->createQueryApi();
$query = "from(bucket: \"$bucket\")
	|> range(start: -12h)
	|> filter(fn: (r) => r._measurement == \"temp_c\")";

$tables = $queryApi->query($query, $org);

$records = [];
foreach ($tables as $table) {
	foreach ($table->records as $record) {
    	// because we will have multiple fields at the same second in time, we need to merge the data into a single array after we query it out
    	$row = key_exists($record->getTime(), $records) ? $records[$record->getTime()] : [];
    	$records[$record->getTime()] = array_merge($row, [$record->getField() => $record->getValue()]);
	}
}

Conclusion

Hopefully, this has given you some clarity around using InfluxDB with a PHP application or script. There is a lot more that InfluxDB and the PHP client can do, so if you are interested in learning more, see the official documentation.

About the author:

Cmeraon Pavey shows how to get started with InfluxDB and PHP

Cameron is a full-stack dev living and working in Melbourne. He's committed himself to the never-ending journey of understanding the intricacies of quality code, developer productivity, and job satisfaction.