All examples assume composer require banguncode/php-dp and require 'vendor/autoload.php';.
use Dp\Fmd;
use Dp\RawSample;
$fmd = new Fmd(); // resolves libdpfj.so / libdpfpdd.so via ldconfig
// $rawJsonSamples = 4 WebSDK-style Raw JSON strings from the SAME finger
$raws = [];
$width = $height = $dpi = 0;
foreach ($rawJsonSamples as $json) {
$sample = RawSample::fromWebSdkJson($json);
$raws[] = $sample['raw'];
$width = $sample['width'];
$height = $sample['height'];
$dpi = $sample['dpi'];
}
$templateBase64 = $fmd->enrollFourToBase64($raws, $width, $height, $dpi);
// -> store $templateBase64 (e.g. in your database)$templateBase64 = $fmd->enrollFourImagesToBase64([
base64_encode(file_get_contents('finger-1.png')),
base64_encode(file_get_contents('finger-2.png')),
base64_encode(file_get_contents('finger-3.png')),
base64_encode(file_get_contents('finger-4.png')),
], dpi: 500); // DPI can't be read from the image — use your scanner's real DPIuse Dp\RawSample;
$sample = RawSample::fromWebSdkJson($probeJson);
$storedTemplateBinary = base64_decode($storedTemplateBase64, true);
$result = $fmd->verify(
$sample['raw'],
$sample['width'],
$sample['height'],
$storedTemplateBinary,
$sample['dpi']
);
if ($result['match']) {
echo "Match! score={$result['score']} (threshold={$result['threshold']})\n";
} else {
echo "No match. score={$result['score']}\n";
}Control the match sensitivity via the target False Accept Rate:
$fmd = new Fmd(far: 1_000_000); // stricter: 1 false accept in a million$result = $fmd->compare($enrollmentFmdBytes, $probeFmdBytes);By default this assumes $enrollmentFmdBytes is FmdFormat::REGISTRATION
(the output of enrollFour()/enrollMany()) and $probeFmdBytes is
FmdFormat::VERIFICATION (the default output of fmdFromRaw()/fmdFromImage()).
Override both if you're comparing standard ISO/ANSI interchange templates
instead:
use Dp\FmdFormat;
$result = $fmd->compare($isoTemplateA, $isoTemplateB, FmdFormat::ISO, FmdFormat::ISO);If you want finer control than the Fmd facade gives you (e.g. you're
managing your own enrollment sample count instead of a fixed 4), the
lower-level classes are available directly:
use Dp\NativeMethods;
use Dp\FeatureExtraction;
use Dp\Enrollment;
use Dp\Comparison;
use Dp\FmdFormat;
$native = new NativeMethods(); // loads libs + selects the matcher engine
// Pass raw samples straight through — Enrollment::createEnrollmentFmd()
// extracts each sample's FMD *inside* the active enrollment session.
// Building them beforehand (outside the session) causes
// dpfj_create_enrollment_fmd() to fail with ENROLLMENT_NOT_READY even
// after every sample was accepted — see docs/troubleshooting.md.
$enrollmentFmd = Enrollment::createEnrollmentFmd($native, $rawSamples, $width, $height, $dpi);
$probeFmd = FeatureExtraction::createFmdFromRaw(
$native, $probeRaw, $width, $height, $dpi, FmdFormat::VERIFICATION
);
$result = Comparison::compare($native, $enrollmentFmd, FmdFormat::REGISTRATION, $probeFmd, FmdFormat::VERIFICATION);$fmd->native() also exposes the same NativeMethods instance if you
constructed via the Fmd facade and want to drop down for one call.
Every native failure throws Dp\SDKException, which carries a typed
Dp\ResultCode enum:
use Dp\SDKException;
use Dp\ResultCode;
try {
$fmd->enrollFourToBase64($raws, $width, $height, $dpi);
} catch (SDKException $e) {
if ($e->resultCode === ResultCode::TOO_SMALL_AREA) {
// ask the user to re-scan, cover more of the sensor
}
throw $e;
}Input validation errors (wrong array counts, malformed JSON, raw length
mismatches) throw plain \InvalidArgumentException, not SDKException —
they never reach the native library at all.