# PHP functions that answer questions about text

One `ThinkThen` class over the C library, through PHP 8.3 FFI. Each call returns its answer and its run facts.

## Install

| Channel | Note |
| --- | --- |
| `composer require botassembly/thinkthen` |  |
| `thinkthen-php-VERSION-TARGET.tar.gz` | The package source, from each release. |
| `thinkthen-c-VERSION-TARGET.tar.gz` | The C library it calls, from the same release. |

Set `THINKTHEN_API_KEY` in the environment.

## A first call

```
<?php
declare(strict_types=1);
require __DIR__ . '/thinkthen-php/autoload.php';

$library = __DIR__ . '/thinkthen-c/lib/libthinkthen.so';
$tt = new ThinkThen($library);

$question = 'Does the customer ask for a refund?';
$isRefund = $tt->decide(
    $question,
    'Please refund my order. It arrived broken.',
);
$outcome = $isRefund['value']['outcome'];
assert($outcome === ThinkThen::YES);

$refund = '{"decide": "' . $question . '", '
    . '"threshold": "0.2:0.8"}';
$backIsRefund = $tt->decide(
    $refund,
    'I want to send this back.',
);
$backOutcome = $backIsRefund['value']['outcome'];
assert($backOutcome === ThinkThen::UNSURE);
$tt->close();
```

*Run it beside the unpacked archives, thinkthen-c/ and thinkthen-php/*

```
php -d ffi.enable=1 \
    -d zend.assertions=1 \
    first-call.php
```

Not sure comes back as `ThinkThen::UNSURE`. Branch on it and send those cases to a person.

## Run facts

`decide` returns an array. Its `value` holds the answer, and its `facts` holds this call's run facts as an array.

## Errors

A failed call throws a `ThinkThenFailure`. Its `kind` names usage, backend, deadline, local, cancelled or defect, and it carries `retryable` and the message.

## Settings and recording

`new ThinkThen($library, $settingsJson)` takes the settings as JSON. `"record"` writes a recording to a folder. `"replay"` answers from that recording with no connection. The [Settings](/install/settings/) page lists every setting.

## Jev, Liquid d1 and Ollama from PHP

PHP reads its backend from the environment. Set `THINKTHEN_BACKEND` to the name below. Set the backend's key variable in the shell, and the same sample asks that backend.

| Backend | Name | Key variable |
| --- | --- | --- |
| [TypeSafe Jev](/install/backends/typesafe/) | `typesafe` | `TYPESAFE_API_KEY` |
| [Liquid d1](/install/backends/liquid/) | `liquid` | `LIQUIDAI_API_KEY`, then `LIQUID_API_KEY` |
| [Ollama](/install/backends/ollama/) | `ollama` | `OLLAMA_API_KEY`. A local address needs no key. |

*Ask TypeSafe Jev.*

```
export THINKTHEN_BACKEND=typesafe
export TYPESAFE_API_KEY=...
```

*Ask Liquid d1.*

```
export THINKTHEN_BACKEND=liquid
export LIQUIDAI_API_KEY=...
```

*Ask Ollama. This block names a second port, 11535, as the site's runs do. On the default port, leave THINKTHEN_BASE_URL out.*

```
export THINKTHEN_BACKEND=ollama
export THINKTHEN_BASE_URL=http://localhost:11535/v1
```

```
<?php
declare(strict_types=1);
require __DIR__ . '/thinkthen-php/autoload.php';

$library = __DIR__ . '/thinkthen-c/lib/libthinkthen.so';
$tt = new ThinkThen($library);

$question = 'Does the customer ask for a refund?';
$brokenIsRefund = $tt->decide(
    $question,
    'Please refund my order. It arrived broken.',
);
$thanksIsRefund = $tt->decide(
    $question,
    'Thanks for the quick help yesterday!',
);
$broken = $brokenIsRefund['value']['outcome'];
$thanks = $thanksIsRefund['value']['outcome'];
assert($broken === ThinkThen::YES);
assert($thanks === ThinkThen::NO);
$tt->close();
```

*Run it beside the unpacked archives, thinkthen-c/ and thinkthen-php/*

```
php -d ffi.enable=1 \
    -d zend.assertions=1 \
    backends.php
```

The configuration file's `backend` names a default backend for every run. The [Configuration](/install/configuration/#configuration-file) page shows the file.

### OpenAI Decisions API from PHP

OpenAI announced its Decisions API on 2026-09-29 in its [DevDay 2026 recap](https://openai.com/index/devday-2026-recap/). OpenAI has published no address, schema or price, so ThinkThen cannot call it. The [OpenAI Decisions API](/install/backends/openai/) page says what ThinkThen knows.

## What is different in PHP

- PHP calls the C library through FFI. `php` takes `-d ffi.enable=1`.
- `new ThinkThen` takes the C library's absolute path.
- `outcome` holds 1, 0 or 2. `ThinkThen::YES`, `NO` and `UNSURE` name them.
- `assert` runs only with `-d zend.assertions=1`.

On GitHub: [github.com/botassembly/thinkthen](https://github.com/botassembly/thinkthen)
