⚙️ Snippets Sammlung
copyrightYear
<?php
# [[!copyrightYear]]
# [[!copyrightYear? &start=`2025`]]
$firstYear = !empty($start) ? (int) $start : (int) date('Y');
$currentYear = (int) date('Y');
if ($firstYear === $currentYear) {
$output = (string) $currentYear;
} else {
$output = $firstYear . ' - ' . $currentYear;
}
return $output;
today_year
<?php
$today = date("Y");
return $today;
SiteLastUpdated
# V 2025-12-22
# Snippet Beispiel:
# [[!SiteLastUpdated:strtotime:date=`%Y-%m-%d`? &context=`web2`]]
use MODX\Revolution\modX;
use MODX\Revolution\modResource;
if (!function_exists('getLastUpdated'))
{
function getLastUpdated($sort, $scriptProperties)
{
global $modx;
// Sicherheitscheck: modX-Instanz vorhanden?
if (!($modx instanceof modX)) {
return null;
}
$context = $modx->getOption('context', $scriptProperties, '');
$classname = modResource::class;
$c = $modx->newQuery($classname);
$c->where(array('published' => 1));
if (!empty($context)) {
$c->where(array('context_key' => $context));
}
$c->sortby($sort, 'DESC');
$c->limit(1);
$resource = $modx->getObject($classname, $c);
if (!$resource) {
// Kontext existiert nicht oder ist leer
return null;
}
return $resource->get($sort);
}
}
$editedon = getLastUpdated('editedon', $scriptProperties);
$createdon = getLastUpdated('createdon', $scriptProperties);
// Wenn gar nichts gefunden wurde, nichts ausgeben (verhindert Fehler)
if (empty($editedon) && empty($createdon)) {
return '';
}
// Vergleich als Integer, falls Werte als String kommen
$editedonInt = (int) $editedon;
$createdonInt = (int) $createdon;
return ($createdonInt > $editedonInt) ? $createdon : $editedon;
emailMask
<?php
# email-Adressen vor bösen bots durch Maskierung verbergen
# [[emailMask? &input=`name.@domain.tld`]]
$hiddenEmail = '';
$length = strlen($input);
for($i = 0;$i<$length;$i++){
$hiddenEmail .= '&#'.ord($input[$i]).';';
}
return $hiddenEmail;
Das Problem:
- Moderne Bots decodieren HTML-Entities ohne Aufwand
- Viele Crawler nutzen fertige Regex dafür
- Browser zeigen im gerenderten DOM wieder die echte E-Mail an → keine wirksame Verschleierung
Die Lösung:
Ein Formular, das E-Mail-Adressen vor der Ausgabe echt verschlüsselt und erst beim Absenden wieder entschlüsselt.
Siehe: FormIt (Kontaktformular) hier im Wiki.
limitWholeWords - Alt!
- Nicht HTML-sicher, HTML-Tags können beschädigt werden.
- Zur sicheren Nutzung immer mit dem Output Filter
notagskombinieren.
<?php
# V 2025-12-22
# z.B.: [[*content:notags:limitWholeWords=`100`]]
# Begrenzt den ausgegebenen Content auf eine maximale Zeichenlänge,
# wobei das letzte Wort nicht abgeschnitten wird.
# Zur sicheren Nutzung immer mit dem Output Filter notags kombinieren.
$limit = (int) $options;
$output = (string) $input;
if ($limit <= 0) {
return '';
}
if (strlen($output) <= $limit) {
return $output;
}
$cut = substr($output, 0, $limit + 1);
$pos = strrpos($cut, ' ');
if ($pos === false) {
return substr($output, 0, $limit);
}
return substr($output, 0, $pos);
limitWholeWords - Neu!
Das neue limitWholeWords kann sowohl mit als auch ohne den Output Filter notags verwendet werden.
<?php
# V 2025-12-23
# z.B.: [[*content:limitWholeWords=`250`]]
# Kürzt den Content auf eine maximale Zeichenlänge,
# wobei das letzte Wort nicht abgeschnitten wird.
#
# HTML-sicher: Solange DOMDocument das HTML erfolgreich parsen kann,
# werden ausschließlich Textknoten gekürzt; HTML-Tags bleiben intakt
# und werden korrekt geschlossen.
#
# Stabilität: Es werden keine globalen Funktionen definiert,
# da Output-Filter pro Seite mehrfach ausgeführt werden können
# (vermeidet Fatal Error: Cannot redeclare ...).
#
# Fallback: Falls HTML-Parsing nicht möglich ist oder fehlschlägt,
# wird automatisch eine einfache Zeichenkürzung ohne HTML-Schutz verwendet,
# um HTTP-500-Fehler zu vermeiden.
#
# Voraussetzung für HTML-Sicherheit: PHP-Erweiterungen dom und libxml.
# Hinweis: mbstring ist optional; ohne mbstring erfolgt die Längenmessung bytebasiert.
$limit = (int) $options;
$html = (string) $input;
if ($limit <= 0 || $html === '') {
return '';
}
$u_strlen = function (string $s): int {
return function_exists('mb_strlen') ? mb_strlen($s, 'UTF-8') : strlen($s);
};
$u_substr = function (string $s, int $start, int $len): string {
return function_exists('mb_substr') ? mb_substr($s, $start, $len, 'UTF-8') : substr($s, $start, $len);
};
$u_strrpos_space = function (string $s) {
if (function_exists('mb_strrpos')) {
return mb_strrpos($s, ' ', 0, 'UTF-8');
}
return strrpos($s, ' ');
};
$fallback_truncate_plain = function (string $text, int $limit) use ($u_strlen, $u_substr, $u_strrpos_space): string {
if ($u_strlen($text) <= $limit) return $text;
$cut = $u_substr($text, 0, $limit + 1);
$pos = $u_strrpos_space($cut);
if ($pos === false) return $u_substr($text, 0, $limit);
return $u_substr($text, 0, (int)$pos);
};
if ($u_strlen($html) <= $limit) {
return $html;
}
# DOM nicht verfügbar -> Fallback
if (!class_exists('DOMDocument') || !class_exists('DOMXPath')) {
return $fallback_truncate_plain($html, $limit);
}
libxml_use_internal_errors(true);
$doc = new DOMDocument('1.0', 'UTF-8');
$wrapped = '<div id="__wrap__">' . $html . '</div>';
if (function_exists('mb_convert_encoding')) {
$wrapped = mb_convert_encoding($wrapped, 'HTML-ENTITIES', 'UTF-8');
}
$ok = $doc->loadHTML($wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
if (!$ok) {
return $fallback_truncate_plain($html, $limit);
}
$wrap = $doc->getElementById('__wrap__');
if (!$wrap) {
return $fallback_truncate_plain($html, $limit);
}
$xpath = new DOMXPath($doc);
$textNodes = $xpath->query('.//text()', $wrap);
$count = 0;
$cutDone = false;
if ($textNodes) {
foreach ($textNodes as $node) {
if ($cutDone) {
if ($node->parentNode) $node->parentNode->removeChild($node);
continue;
}
$text = $node->nodeValue;
if ($text === null || $text === '') continue;
$len = $u_strlen($text);
if ($count + $len <= $limit) {
$count += $len;
continue;
}
$remain = $limit - $count;
if ($remain <= 0) {
$node->nodeValue = '';
$cutDone = true;
continue;
}
$slice = $u_substr($text, 0, $remain + 1);
$pos = $u_strrpos_space($slice);
if ($pos !== false && $pos > 0) {
$slice = $u_substr($slice, 0, (int)$pos);
} else {
$slice = $u_substr($text, 0, $remain);
}
$node->nodeValue = rtrim($slice);
$cutDone = true;
}
}
# Leere List-Items entfernen (verhindert leere Bullets nach dem Kürzen)
$emptyLis = $xpath->query('.//li[not(normalize-space(.)) and count(.//img|.//br|.//hr|.//input|.//video|.//audio|.//iframe|.//svg)=0]', $wrap);
if ($emptyLis) {
for ($i = $emptyLis->length - 1; $i >= 0; $i--) {
$li = $emptyLis->item($i);
if ($li && $li->parentNode) {
$li->parentNode->removeChild($li);
}
}
}
# Leere UL/OL entfernen, falls nach dem Entfernen der LI nichts mehr drin ist
$emptyLists = $xpath->query('.//ul[not(li)] | .//ol[not(li)]', $wrap);
if ($emptyLists) {
for ($i = $emptyLists->length - 1; $i >= 0; $i--) {
$lst = $emptyLists->item($i);
if ($lst && $lst->parentNode) {
$lst->parentNode->removeChild($lst);
}
}
}
$out = '';
foreach ($wrap->childNodes as $child) {
$out .= $doc->saveHTML($child);
}
if ($out === '') {
return $fallback_truncate_plain($html, $limit);
}
return $out;
myIP
<?php
// IP Adresse!
if (isset($_SERVER['REMOTE_ADDR'])) $ipCode=$_SERVER['REMOTE_ADDR'];
else $ipCode='0:0:0:0:0:0:0:0';
return $ipCode;