# Landing page developer guidelines

This document contains technical tips and rules for developers who create landing pages for work in the tracker-sandbox environment.

# Using curl in PHP

To prevent scripts from being interrupted unexpectedly, it is important to consider the execution time limits:

  • The maximum time for a PHP script to run is set via the TRACKER_LANDING_PAGES_TIMEOUT variable (in seconds).

  • If a curl request runs longer than this time, it is forcibly interrupted.

  • Make sure to specify the timeout via CURLOPT_TIMEOUT — the value is 1 second less than TRACKER_LANDING_PAGES_TIMEOUT.

Example of setting the timeout:

$systemTimeout = getenv('TRACKER_LANDING_PAGES_TIMEOUT') ?: 10;
$curlTimeout = max(1, $systemTimeout - 1);

$ch = curl_init('https://example.com/api');

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $curlTimeout);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo 'Curl error: ' . curl_error($ch);
} else {
echo 'Response: ' . $response;
}

curl_close($ch);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Timeouts in curl are especially important when working with slow external APIs or when chaining curl calls inside a landing page.

# Using Guest Redis

The tracker-sandbox container has a built-in Redis service intended only for temporary data storage, debugging, and testing. Use it if caching in memory, or files is not suitable.

# Features

  • Redis is only available locally, inside the container.

  • Connection is made using authorization data, which the system provides through environment variables.

  • Store data in Redis only temporarily — it can be deleted or overwritten.

Timeouts in curl are especially important when working with slow external APIs or when using a chain of curl calls inside a landing page.

# Example of connecting to PHP 8.3 using phpredis

<?php

$redis = new Redis();

try {
$redisHost = getenv('GUEST_REDIS_HOST');
$redisPort = getenv('GUEST_REDIS_PORT');
$redisUser = getenv('GUEST_REDIS_KEITARO_USER');
$redisPassword = getenv('GUEST_REDIS_KEITARO_PASSWORD');

$redis->connect($redisHost, $redisPort);

if (!$redis->auth([$redisUser, $redisPassword])) {
throw new RedisException("Redis authorization error: invalid username or password");
}

$redis->set('example_key', 'hello world');
$value = $redis->get('example_key');

echo "Received value: " . $value . PHP_EOL;

} catch (RedisException $e) {
echo "Redis error: " . $e->getMessage() . PHP_EOL;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# Discard file_put_contents in favor of Redis

Using the file system (file_put_contents(), fopen(), fwrite(), etc.) is not recommended for the following reasons:

  • Files may conflict with parallel requests.

  • Temporary files are not guaranteed to be saved after the request is completed.

  • File system may be cleaned or reinitialized between container restarts.

  • Potential issues with permissions and open_basedir restrictions.

It is recommended to use guest Redis as a safe and fast storage for temporary data.

# Example: replacing file_put_contents

Produced:

$data = json_encode(['foo' => 'bar']);
file_put_contents('/tmp/my-cache.json', $data);
1
2

Now:

$redis->set('my-cache-key', json_encode(['foo' => 'bar']));
1

# Example: replacing file_get_contents

Produced:

$data = file_get_contents('/tmp/my-cache.json');
$array = json_decode($data, true);
1
2

Now:

$data = $redis->get('my-cache-key');
$array = json_decode($data, true);
1
2

Use logical keys, possibly with prefixes (landing:lead_data:123), to avoid overlaps and simplify debugging.

If data needs to be stored for a limited time, use setex():

$redis->setex('temp:token', 60, 'abc123'); // expires in 60 seconds
1