Skip to main content

⚙️ 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 notags kombinieren.
<?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.

  • Ohne notags bleibt HTML erhalten; die Kürzung ist HTML-sicher, sofern das DOM-Parsing erfolgreich ist.
  • Mit notags wird der Content vorab von HTML befreit; die Ausgabe ist reiner Text und entspricht funktional dem alten Snippet.
  • Ein separates altes Snippet ist nicht erforderlich, da das neue beide Anwendungsfälle abdeckt.
<?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;