Skip to main content

📸 Eigenes pThumb-Snippet

Eigenes pThumb-Snippet für MODX 3.1.x+ und PHP 8.2+

pThumb ist eine moderne Weiterentwicklung von phpThumbOf mit besserem Caching-Mechanismus.

In Templates / Chunks verwendest du z.B.:

[[!pthumb? &input=`assets/images/meinbild.jpg` &options=`w=300&h=200&zc=1`]]

Leider wird pThumb faktisch seit Jahren nicht mehr wirklich gepflegt.

Also bauen wir unser eigenes pthumb Snippet, das die gewohnte Syntax beibehält.


✅ Kompatibilität

  • MODX 3.1.x+
  • PHP 8.2+

💡 Idee

Du ersetzt den alten pThumb-Snippet-Eintrag im Manager durch ein eigenes Snippet mit demselben Namen.
Dieses neue Snippet verwendet direkt die in MODX integrierte modPhpThumb-Klasse (Namespace MODX\Revolution), die unter MODX 3 aktiv gepflegt wird und PHP-8-kompatibel ist.

So bleiben alle bestehenden Aufrufe weiterhin funktionsfähig, zum Beispiel:

[[!pthumb? &input=`assets/images/meinbild.png` &options=`w=300&h=200&zc=1`]]

Du musst also weder Templates noch Inhalte ändern.

Gleichzeitig stellt deine angepasste Version sicher:

  • PNG- und GIF-Bilder behalten ihre Transparenz (Alpha)
  • das Ausgabeformat bleibt standardmäßig beim Original (außer du setzt explizit f=jpg, f=png, … in &options)
  • Thumbnails werden wie gewohnt skaliert (w, h, zc)
  • Probleme durch alte Thumbnails lassen sich einfach beheben, indem du den Cache-Ordner assets/images-cache/ leerst

Hinweis:
Wenn dein Snippet das alte phpthumbof ersetzen soll, muss es ebenfalls phpthumbof heißen.
Du musst sonst nichts weiter beachten, außer dass du dann genau diesen Namen in Templates oder Chunks aufrufst.
Es ist auch möglich, beide Varianten parallel zu nutzen: ein Snippet pthumb und zusätzlich ein Snippet phpthumbof.

🛠️ Beispiel-Snippet pthumb (Basis-Drop-In)

In MODX einen neuen Snippet mit dem Namen pthumb anlegen
(Inhalt z.B. so, Pfadnamen kannst du bei Bedarf anpassen):

<?php
/**
 * pthumb (eigener Ersatz für das alte pThumb / phpThumbOf)
 * V 2025-12-05
 *
 * Hinweis:
 *   Wenn dieses Snippet das alte "phpthumbof" ersetzen soll, muss es auch
 *   "phpthumbof" heißen. Alternativ können beide parallel existieren
 *   (pthumb + phpthumbof).
 *
 * Ziel:
 *   [[!pthumb? &input=`assets/images/meinbild.jpg` &options=`w=300&h=200&zc=1`]]
 *   -> gibt eine Thumbnail-URL zurück (exakt 300x200, mit Beschnitt)
 *
 *   Beispiel ohne Beschnitt, Seitenverhältnis bleibt erhalten:
 *   [[!pthumb? &input=`assets/images/meinbild.jpg` &options=`w=300`]]
 *   -> verkleinert proportional auf 300px Breite (Höhe wird automatisch berechnet)
 *
 * Transparenz / Ausgabeformat:
 *   - PNG-, GIF- und WebP-Quellen bleiben standardmäßig in ihrem Format,
 *     damit Alpha-Transparenz erhalten bleibt.
 *   - Das Ausgabeformat kann explizit über &options (`f=jpg`, `f=png`, ...) geändert werden.
 *   - JPEG-Quellen bleiben standardmäßig JPEG.
 *
 * Voraussetzungen:
 *   - MODX 3.1.x oder höher
 *   - PHP 8.1.x oder höher
 */

use MODX\Revolution\modPhpThumb;

$input         = $modx->getOption('input', $scriptProperties, '');
$optionsString = $modx->getOption('options', $scriptProperties, '');
$toPlaceholder = $modx->getOption('toPlaceholder', $scriptProperties, '');
// lokaler Pfad
$cacheDir      = $modx->getOption('pthumb_cache_path', $scriptProperties, 'assets/images-cache');

// nichts zu tun
if ($input === '') {
    return '';
}

// Basispfade
$basePath     = MODX_BASE_PATH;
$baseUrl      = $modx->getOption('base_url', null, '/');
$cacheDirTrim = trim($cacheDir, '/');

// CacheManager holen (für writeTree)
$cacheManager = $modx->getCacheManager();

// Optionsstring etwas normalisieren (&amp; -> &)
$optionsNormalized = str_replace('&amp;', '&', $optionsString);

// Optionen in Array parsen (einfach, ohne Arrays wie fltr[])
$params = [];
if ($optionsNormalized !== '') {
    $pairs = explode('&', $optionsNormalized);
    foreach ($pairs as $pair) {
        if ($pair === '') {
            continue;
        }
        $kv = explode('=', $pair, 2);
        $key = $kv[0];
        $val = $kv[1] ?? '';
        $params[$key] = $val;
    }
}

// Quelldatei auf Dateisystempfad abbilden
$src = $input;

// Relativer Pfad -> an BASE_PATH hängen
$srcFs = $basePath . ltrim($input, '/');
$src   = $srcFs;

if (is_file($srcFs)) {
    $mtime = filemtime($srcFs) ?: 0;
} else {
    $mtime = 0;
    $modx->log(modX::LOG_LEVEL_ERROR, '[pthumb] Quelldatei nicht gefunden: ' . $srcFs);
    // Fallback: Original-URL zurückgeben
    return $input;
}

// Dateiendung bestimmen
$pathInfo  = pathinfo($input);
$ext       = strtolower($pathInfo['extension'] ?? '');
$outputExt = $ext;

/**
 * Transparenz erhalten:
 * Wenn Quellbild PNG/GIF/WebP ist und kein Ausgabeformat vorgegeben wurde
 * (weder f= noch fm=), erzwingen wir dasselbe Format als Ausgabeformat.
 */
if (in_array($ext, ['png', 'gif', 'webp'], true)
    && empty($params['f'])
    && empty($params['fm'])) {
    $params['f'] = $outputExt = $ext;
}

// pThumb/phpThumbOf-Kompatibilität: f= / fm= ändern das Ausgabeformat
if (!empty($params['f'])) {
    $outputExt = strtolower($params['f']);
}
if (!empty($params['fm'])) {
    $outputExt = strtolower($params['fm']);
}

// Cache-Dateinamen erzeugen (ähnliche Idee wie pThumb: originaler Name + Hash)
// $hash = md5($input . '|' . $optionsNormalized);
$hash = md5($input . '|' . $optionsNormalized . '|' . $mtime);

$relativeDir   = ltrim($pathInfo['dirname'] ?? '', './\\');
$relativeDir   = ($relativeDir === '' || $relativeDir === '.') ? '' : $relativeDir . '/';
$cacheFileName = $pathInfo['filename'] . '.' . $hash . '.' . $outputExt;

$cacheRelPath = $cacheDirTrim . '/' . $relativeDir . $cacheFileName;
$cacheAbsPath = $basePath . $cacheRelPath;
$cacheUrl     = $baseUrl . $cacheRelPath;

// Wenn Thumbnail schon existiert -> direkt ausgeben
if (is_file($cacheAbsPath)) {
    if ($toPlaceholder !== '') {
        $modx->setPlaceholder($toPlaceholder, $cacheUrl);
        if (!empty($params['w'])) {
            $modx->setPlaceholder($toPlaceholder . '.width', (int)$params['w']);
        }
        if (!empty($params['h'])) {
            $modx->setPlaceholder($toPlaceholder . '.height', (int)$params['h']);
        }
        return '';
    }
    return $cacheUrl;
}

// Thumbnail über modPhpThumb erzeugen
try {
    $phpThumb = new modPhpThumb($modx);
} catch (\Throwable $e) {
    // Fallback: originalen Pfad zurückgeben
    $modx->log(modX::LOG_LEVEL_ERROR, '[pthumb] Konnte modPhpThumb nicht instanzieren: ' . $e->getMessage());
    return $input;
}

$phpThumb->initialize();
$phpThumb->set($src);

// alle bekannten Parameter durchreichen
foreach ($params as $key => $value) {
    if ($key === '' || $value === '') {
        continue;
    }

    if ($key === 'fm') {
        $phpThumb->setParameter('f', $value);
    } else {
        $phpThumb->setParameter($key, $value);
    }
}

// Zielverzeichnis anlegen
$cacheManager->writeTree(dirname($cacheAbsPath) . '/');

// Thumbnail erzeugen und speichern
if ($phpThumb->GenerateThumbnail()) {
    if ($phpThumb->RenderToFile($cacheAbsPath)) {
        if ($toPlaceholder !== '') {
            $modx->setPlaceholder($toPlaceholder, $cacheUrl);
            if (!empty($params['w'])) {
                $modx->setPlaceholder($toPlaceholder . '.width', (int)$params['w']);
            }
            if (!empty($params['h'])) {
                $modx->setPlaceholder($toPlaceholder . '.height', (int)$params['h']);
            }
            return '';
        }
        return $cacheUrl;
    } else {
        $modx->log(modX::LOG_LEVEL_ERROR, '[pthumb] RenderToFile() fehlgeschlagen für ' . $cacheAbsPath);
    }
} else {
    $modx->log(modX::LOG_LEVEL_ERROR, '[pthumb] GenerateThumbnail() fehlgeschlagen für ' . $src);
}

// Im Fehlerfall: originalen Pfad zurückgeben
return $input;

📄 Verwendung im Template / in Chunks

Einfaches Beispiel:

<img src="[[!pthumb? &input=`assets/images/meinbild.jpg` &options=`w=300&h=200&zc=1`]]" alt="">

Mit Platzhaltern:

[[!pthumb?
    &input=`[[+tv.image]]`
    &options=`w=300&h=200`
    &toPlaceholder=`thumb`
]]

<img src="[[+thumb]]" width="[[+thumb.width]]" height="[[+thumb.height]]" alt="">

⚠️ Wichtige Hinweise / Grenzen

🔧 Snippet-Name

  • Wenn das alte pThumb-Extra noch installiert ist, gibt es vermutlich schon ein Snippet pthumb.
  • Das musst du löschen oder umbenennen, bevor du dein eigenes pthumb anlegst, sonst gibt es Konflikte.

🧩 Kompatibilität

  • Standardoptionen wie w, h, zc, q, f und fm funktionieren vollständig mit dem Snippet.
  • Komplexere Filter wie fltr[]=stc|ffffff werden im aktuellen Beispiel nicht speziell verarbeitet.
  • Wenn du solche erweiterten Filter intensiv benötigst, müsste die Options-Parsing-Logik entsprechend erweitert werden.

📁 Cache-Pfad

  • Aktuell: assets/images-cache/...
  • Kannst du über die Snippet-Property pthumb_cache_path anpassen, wenn du einen anderen Pfad brauchst (z.B. um eine alte pThumb-Cache-Struktur nachzubauen).

🧱 MODX-/PHP-Stand

  • MODX 3.1.x ist offiziell mit PHP 8.2 getestet.
  • modPhpThumb ist Teil des Cores und wird mitgepflegt, also deutlich zukunftssicherer als das alte pThumb-Extra.

Damit kannst du deine bestehenden Aufrufe von [[!pthumb? ...]] weiter benutzen, ohne alle Chunks/Template-Codes anfassen zu müssen.


🖼️ Wie Bilder mit GD gecached werden

GD selbst erstellt keinen Cache, sondern verarbeitet nur das Bild (laden, skalieren, speichern).
Der eigentliche Cache entsteht durch das Speichern des erzeugten Thumbnails auf der Festplatte.

Ablauf:

  1. Beim ersten Aufruf wird das Bild über GD verarbeitet
  2. Das Snippet speichert das fertige Thumbnail unter:
    assets/images-cache/...
  3. Beim nächsten Aufruf prüft das Snippet:
    Wenn die Datei bereits existiert → direkt zurückgeben, kein GD mehr nötig

Damit verbraucht nur der erste Aufruf CPU, alle weiteren nutzen die gespeicherte Thumbnail-Datei als Cache.


✅ Ergebnis

  • bs5GalleryModal bleibt unverändert
  • Thumbnails werden über pthumb gecached
  • Cache liegt in assets/images-cache/ und kann bei Bedarf bedenkenlos gelöscht werden