Es können mehrere Erinnerung an verschiedenen Tagen vor dem jeweiligen Geburtstag verschickt werden, z.B. eine Woche vorher, um ggf. noch ein Geschenk zu besorgen und am Tag des Geburtstags selbst. Betreff und Inhalt können jeweils frei angepasst werden. Alle Einstellungen und Erklärungen im entsprechenden Abschnitt im Script.
Das Script ist dazu gedacht, täglich als cron-job ausgeführt zu werden. Aktuell getestet mit der Datenbank von Admidio 5.0.14 und PHP-cli 7.4 und 8.4.
Vielleicht hilft es dem einen oder anderen.
Code: Alles auswählen
#!/bin/php
<?php
// Admidio-cron-script to send birtday reminiders via email
// Make sure this script runs at an early hour so "today" reminders (offest 0) are not sent too late
// Run from cron or similar like: ionice -c3 nice -15 /usr/bin/php7.4 -d memory_limit=256M /path/to/this-script.php
//
// Licence: CC-BY-NC-SA https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
//
// Admidio-Cron-Script zum Versenden von Geburtstagsinfos per E-Mail
// Das Script sollte möglichst früh am Tag laufen, damit Erinnerungen "heute" (offset 0) nicht zu spät verschickt werden
// Via Cron o.ä. ausführen per: ionice -c3 nice -15 /usr/bin/php7.4 -d memory_limit=256M /pfad/zu/diesem-script.php
//
// Lizenz: CC-BY-NC-SA https://creativecommons.org/licenses/by-nc-sa/4.0/deed.de
//
date_default_timezone_set('Europe/Berlin');
define('THIS_LOCALE', 'de_DE');
setlocale(LC_ALL, THIS_LOCALE);
setlocale(LC_TIME, THIS_LOCALE);
mb_internal_encoding('UTF-8');
define('VERSION', '2026-09-08 obengelb GmbH');
// Begin of settings section | Beginn der Einstellungen
// ----------------------------------------------------
// Default date format
// ISO date format codes as in https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax (column "pattern")
// Example: EEEE YYYY-MM-dd HH:mm:ss:S
//
// Standard Datumsformat
// ISO Datumsformat-Code wie auf https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax ("Pattern"-Spalte)
// Beispiel: EEEE dd.MM.YYYY HH:mm:ss:S
$dateformat_default = 'dd.MM.YYYY';
// Admidio database connection
// Admidio Datenbankverbindung
$database_host = '...';
$database_user_name = '...';
$database_user_password = '...';
$database_name = '...';
$database_port = '3306';
$database_socket = null;
// Recipient(s). Use comma as delimiter
// Empfänger. Mehrere mit Komma trennen
$recipients = 'empfaenger@example.com';
// Additional email header(s)
// Zusätzlicher E-Mail-Header
$mail_headers = 'From: Erinnerung <nicht-antworten@example.com>' . "\r\n" . 'User-Agent: Admidio birthday reminder' . "\r\n";
// Definition of reminders
// Format:
// days-before-birthday => [
// 'subject' => 'email subject',
// 'body' => 'email body',
// ]
// days-before-birthday 0 is day of birthday itself
// All date placeholders may include a date format definition, see $dateformat_default
//
// Definition der Erinnerungen
// Format:
// Tage-vor-dem-Geburtstag => [
// 'subject' => 'E-Mail Betreff',
// 'body' => 'E-Mail Text',
// ]
// Tage-vor-dem-Geburtstag 0 ist Tag des Geburtstags selbst
// Alle Datums-Platzhalter dürfen um Format ergänzt werden, siehe $dateformat_default
$reminders = [
7 => [
'subject' => 'Erinnerung: [[namefirst]] [[namelast]] wird am [[birthdate_this_year:EEEE]] in [[days_offset]] Tagen [[age]] Jahre alt',
'body' => 'Erinnerung:
[[namefirst]] [[namelast]] wird am [[birthdate_this_year:EEEE]] den [[birthdate_this_year:dd.MM.YYYY]] [[age]] Jahre alt.
--
Diese E-Mail wurde am [[now:dd.MM.YYYY]] um [[now:HH:mm:ss]] automatisch aus den Mitgliederdaten der Vereinsverwaltung erstellt
'
],
0 => [
'subject' => 'Erinnerung: [[namefirst]] [[namelast]] wird heute [[age]] Jahre alt 🎂 ',
'body' => 'Erinnerung:
[[namefirst]] [[namelast]] wurde am [[birthdate:dd.MM.YYYY]] geboren und wird heute [[age]] Jahre alt.
--
Diese E-Mail wurde am [[now:dd.MM.YYYY]] um [[now:HH:mm:ss]] automatisch aus den Mitgliederdaten der Vereinsverwaltung erstellt
'
],
];
// End of settings section | Ende der Einstellungen
// ------------------------------------------------
$query = ' SELECT
u.`usr_id`,
(
select `usd_value`
from `admidio_user_data` as ud
where ud.`usd_usr_id` = u.`usr_id`
and usd_usf_id = 2
) as namefirst,
(
select `usd_value`
from `admidio_user_data` as ud
where ud.`usd_usr_id` = u.`usr_id`
and usd_usf_id = 1
) as namelast,
(
select `usd_value`
from `admidio_user_data` as ud
where ud.`usd_usr_id` = u.`usr_id`
and usd_usf_id = 9
) as birthdate,
(
select `usd_value`
from `admidio_user_data` as ud
where ud.`usd_usr_id` = u.`usr_id`
and usd_usf_id = 11
) as email
FROM `admidio_users` as u
where usr_id > 1
';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli($database_host, $database_user_name, $database_user_password, $database_name, $database_port, $database_socket);
$db_result = $mysqli->query($query);
$mail_headers .= "Content-Transfer-Encoding: 8bit\n";
$mail_headers .= "Content-Type: text/plain; charset=UTF-8\n";
$timezone = new DateTimeZone(date_default_timezone_get());
$now = new DateTimeImmutable('now', $timezone);
$duplicates_check = [];
$rowscount = 0;
while ($row = $db_result->fetch_object()){
if( empty($row->birthdate)) continue;
if( ! preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $row->birthdate)) echo "Invalid birthdate: " . $row->birthdate . "\n";
if( ! preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $row->birthdate)) continue;
$unique_id = implode('--', (array) $row);
if( ! isset($duplicates_check[ $unique_id ])) {
$duplicates_check[ $unique_id ] = $unique_id;
}
$birthdate = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $row->birthdate . ' 00:00:00');
$age = $now->format('Y') - $birthdate->format('Y');
$birthdate_this_year = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', date('Y') . '-' . $birthdate->format('m') . '-' . $birthdate->format('d') . ' 00:00:00');
foreach($reminders as $reminders_key => $reminders_item) {
$reminders_item_parsed = $reminders_item;
$days_offset = $reminders_key;
if(0 == $days_offset) {
$date_to_check = $birthdate_this_year;
} else {
$date_to_check = $birthdate_this_year->sub( new DateInterval('P' . $days_offset . 'D') );
}
if($now->format('Y-m-d') == $date_to_check->format('Y-m-d')) {
$replacements = [
'/\[\[namefirst\]\]/' => $row->namefirst
,'/\[\[namelast\]\]/' => $row->namelast
,'/\[\[age\]\]/' => $age
,'/\[\[days_offset\]\]/' => $days_offset
,'/\[\[birthdate(:(?P<dateformat>.*?))\]\]/' => $birthdate
,'/\[\[birthdate_this_year(:(?P<dateformat>.*?))\]\]/' => $birthdate_this_year
,'/\[\[now(:(?P<dateformat>.*?))\]\]/' => $now
];
foreach($reminders_item as $reminders_item_key => $reminders_item_item) {
foreach($replacements as $replacements_key => $replacements_item) {
$regex = $replacements_key;
// replace only one match at a time to allow same keys with different date formats
while( preg_match($regex, $reminders_item_parsed[$reminders_item_key], $matches) ) {
if($replacements_item instanceof DateTimeImmutable) {
if( ! empty($matches['dateformat'])) {
$dateformat = $matches['dateformat'];
} else {
$dateformat = $dateformat_default;
}
// DONT> will not use current locale: $replacement = $replacements_item->format($dateformat);
$formatter = new IntlDateFormatter(THIS_LOCALE, IntlDateFormatter::SHORT, IntlDateFormatter::SHORT);
$formatter->setPattern($dateformat);
$replacement = $formatter->format($replacements_item);
} else {
$replacement = $replacements_item;
}
$reminders_item_parsed[$reminders_item_key] = preg_replace($replacements_key, $replacement, $reminders_item_parsed[$reminders_item_key], 1); // onyl one replacement
}
} // foreach($replacements)
}
$encoded_subject = mb_encode_mimeheader($reminders_item_parsed['subject'], 'UTF-8', 'B', "\r\n", strlen('Subject: '));
mail($recipients, $encoded_subject, $reminders_item_parsed['body'], $mail_headers);
// Debug to console. Do not use in cron job
// echo " " . $row->namefirst . ' ' . $row->namelast . "\n";
// echo " now: " . $now->format('d.m.Y H:i:s') . "\n";
// echo "birthdate_this_year: " .$birthdate_this_year->format('d.m.Y H:i:s') . "\n";
// echo " days_offset: " .$days_offset . "\n";
// echo " date_to_check: " .$date_to_check->format('d.m.Y H:i:s') . "\n";
// echo " -> DO REMINDER\n";
// echo $reminders_item_parsed['body'] . "\n";
// echo "\n";
}
} // foreach($reminders)
$rowscount++;
}