Retention Curve Builder
Builds day/week/month retention curves from activity events.
Function signature
ogBuildRetentionCurve(events = array(), options = array())
Categories
- Analytics and Reports
Parameters
eventsEvents with user_id, cohort_date, and activity_date keys.optionsOptions: max_periods. Recognized keys: `max_periods`.Return value
Short public-safe status message.
- cohorts
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Builds a simple retention curve from user activity events.
*
* @param array $events Events with user_id, cohort_date, and activity_date keys.
* @param array $options Options: max_periods.
* @return array Retention curve rows keyed by cohort date.
*/
function ogBuildRetentionCurve($events = array(), $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (!is_array($events)) {
$result['message'] = 'Events must be an array.';
return $result;
}
if (!is_array($options)) {
$options = array();
}
$max_periods = 12;
if (!empty($options['max_periods'])) {
$max_periods = (int)$options['max_periods'];
}
if ($max_periods < 1) {
$max_periods = 12;
}
$cohorts = array();
foreach ($events as $event) {
if (!is_array($event) || empty($event['user_id']) || empty($event['cohort_date']) || empty($event['activity_date'])) {
continue;
}
$user_id = (string)$event['user_id'];
$cohort_time = strtotime((string)$event['cohort_date']);
$activity_time = strtotime((string)$event['activity_date']);
if (empty($cohort_time) || empty($activity_time) || $activity_time < $cohort_time) {
continue;
}
$cohort = date('Y-m-d', $cohort_time);
$period = (int)floor(($activity_time - $cohort_time) / 86400);
if ($period > $max_periods) {
continue;
}
if (empty($cohorts[$cohort])) {
$cohorts[$cohort] = array('users' => array(), 'periods' => array());
}
$cohorts[$cohort]['users'][$user_id] = true;
if (empty($cohorts[$cohort]['periods'][$period])) {
$cohorts[$cohort]['periods'][$period] = array();
}
$cohorts[$cohort]['periods'][$period][$user_id] = true;
}
$curve = array();
foreach ($cohorts as $cohort => $data) {
$cohort_size = count($data['users']);
$curve[$cohort] = array('cohort_size' => $cohort_size, 'periods' => array());
for ($period = 0; $period <= $max_periods; $period++) {
$retained = 0;
if (!empty($data['periods'][$period])) {
$retained = count($data['periods'][$period]);
}
$percent = 0;
if ($cohort_size > 0) {
$percent = ($retained / $cohort_size) * 100;
}
$curve[$cohort]['periods'][$period] = array('retained' => $retained, 'percent' => $percent);
}
}
$result['success'] = true;
$result['message'] = 'Retention curve built.';
$result['data'] = array('cohorts' => $curve);
return $result;
}