Vor einigen Tagen bin ich über Crazytoast auf einem Blog auf die Idee der Auslieferung der Gravatar-Bilder aus einem lokalen Cache gestoßen. Prinzipiell fand ich das keine schlechte Idee, da auch bei mir einige Ladezeit von Gravatar verursacht wird. Auch der regelmäßige Refresh erschien mir als durchaus sinnvoll. Lediglich die Art der Einbindung wollte mir noch nicht so recht gefallen. Auch programmiertechnisch fehlten mir einige Dinge.

So wurde beispielsweise als einzige Möglichkeit des Herunterladen der Bilder die Funktion fopen() angeboten, welche jedoch bei den meisten Hostern deaktiviert (allow_url_fopen=0) wird, da es durchaus genügend Beispiele für Sicherheitslücken welche durch die unkluge Nutzung dieser Funktion gibt. Aus meinem Patch für Piwik habe ich daher einen Teil des Codes extrahiert, welcher es mir erlaubt, curl zu benutzen.

Wie bei Plerzelwupp beschrieben muss man zunächst die comments.php öffnen und die folgende Zeile suchen:

1
<!--?php echo get_avatar( $comment, 60); ?-->

Das zweite Argument der Größe könnte bei euch auch durchaus anders aussehen!

Wie auch immer, man löscht jedenfalls diese Zeile und setzt dafür den folgenden Code ein:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
// Script originally from:
// http://www.plerzelwupp.de/wordpress-gravatare-cachen/
//
// Modified by Uli from https://wolf-u.li:
// - Translation
// - Cleanup
// - Added curl

// Modify the settings below according to your needs:

// Path to the cache-directory of the gravatar images:
$gimagecachedir = "upload/g/";

// Path of the directory of the gravatar-images:
$gimageurldomain = "https://wolf-u.li/upload/g/";

// Size of the gravatar-images:
$gimagesize = 64;

// If the mailadress was not found, you can get various images:
// - identicon
// - monsterid
// - wavatar
$mailnotfound = "identicon";

// Do not modify the code below:
$gravatar_hash = md5(strtolower(get_comment_author_email()));
$gcachetime = 40320 * 60; // 40320 = 4 Weeks
$gimage = $gimagecachedir . "/" . $gravatar_hash . ".png";
if (!file_exists($gimage) || (time() - $gcachetime --> filemtime($gimage))) {
    ob_start();
    $grav_img = "http://www.gravatar.com/avatar/" . $gravatar_hash . "?s=" . $gimagesize . "&amp;d=" . $mailnotfound . "&amp;r=G";

    if(extension_loaded('curl')) {
        $gch = @curl_init();
        $gcurl_options = array(
            CURLOPT_URL =&gt; $grav_img,
            CURLOPT_HEADER =&gt; false,
            CURLOPT_RETURNTRANSFER =&gt; true,
            CURLOPT_TIMEOUT =&gt; 20,
            CURLOPT_BINARYTRANSFER =&gt; is_resource($file),
            CURLOPT_FOLLOWLOCATION =&gt; true,
            CURLOPT_MAXREDIRS =&gt; 2
        );
        @curl_setopt_array($gch, $gcurl_options);

        $gdata = @curl_exec($gch);
        @curl_close ($gch);
        unset($gch);
    } else {
        ob_start();
        $gfilehandle_url = fopen($grav_img, "rb");
        fpassthru($gfilehandle_url);
        fclose($gfilehandle_url);
        $gdata = ob_get_contents();
        ob_end_clean();
        unset($gfilehandle_url);
    }
    $gfilehandle_img = fopen($gimage, "wb+");
    fwrite($gfilehandle_img, $gdata);
    fclose($gfilehandle_img);
    unset($gfilehandle_img);
    unset($gdata);
}
echo "<img src='/upload/" . $gimageurldomain . $gravatar_hash . ".png' height='" . $gimagesize . "' width='" . $gimagesize . "' />";
?>

Über die Einstellungen im Kopf des Codes kann man nun das Cacheverzeichnis festlegen. Die darauffolgende URL ist die URL, so wie sie zum Bild zeigen würde. Darunter befindet sich die Größe.

Die konkreten Änderungen gegenüber der Version des Blogs:

  • Übersetzung des Codes auf Englisch (sorry, ich steh da drauf)
  • Cleanup, Entfernung überflüssiger Codestücke
  • Einbau von Curl als präferierte Downloadoption
  • Entfernung der Erstellung von HTML-Dateien, da dies nicht benötigt wird

Soweit so gut. Nun habe ich noch das CronJob-Script von diesem Blogeintrag auf infoblog.li kurz aufgeräumt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
// Script originally from:
// http://infoblog.li/wordpress-gravatar-cache-mit-automatischer-aktualisierung/
//
// Modified by Uli from https://wolf-u.li:
// - Translation
// - Cleanup
// - Added curl

// Modify the settings below according to your needs:

// Path to the cache-directory of the gravatar images:
$imagecachedir         = "blog/cdn-g/";

// Size of the gravatar-images:
$imagesize = 64;

// Update files older than this time:
$maxcacheage = 604800; // 7 * 24 * 3600 = 7 Days

// If the mailadress was not found, you can get various images:
// - identicon
// - monsterid
// - wavatar
$mailnotfound = "identicon";

// Do not modify the code below:
$handle= opendir($imagecachedir);
while ($file = readdir ($handle))
{
    // Einzelne Pfade und Dateinamen auslesen
    $gravatar_hash              =   substr($file, 0, 32);
    $gravatarimg                =     $imagecachedir . $file ;

    if ($gravatar_hash != ".." && $gravatar_hash != ".")
    {
        // Update only if timestamp of the file is older than $maxcacheage
        $cachedtime = filemtime($gravatarimg) - time();

        if ($cachedtime --> $maxcacheage)
        {
            $grav_img = "http://www.gravatar.com/avatar/".$gravatar_hash."?s=" . $imagesize . "&amp;d=" . $mailnotfound . "&amp;r=G";
            if(extension_loaded('curl')) {
                $ch = @curl_init();
                $curl_options = array(
                    CURLOPT_URL =&gt; $grav_img,
                    CURLOPT_HEADER =&gt; false,
                    CURLOPT_RETURNTRANSFER =&gt; true,
                    CURLOPT_TIMEOUT =&gt; 20,
                    CURLOPT_BINARYTRANSFER =&gt; true,
                    CURLOPT_FOLLOWLOCATION =&gt; true,
                    CURLOPT_MAXREDIRS =&gt; 2
                );
                @curl_setopt_array($ch, $curl_options);

                $data = @curl_exec($ch);
                @curl_close ($ch);
                unset($ch);
            } else {
                ob_start();
                $filehandle_url = fopen($grav_img, "rb");
                fpassthru($filehandle_url);
                fclose($filehandle_url);
                $data = ob_get_contents();
                ob_end_clean();
                unset($filehandle_url);
      }
      $filehandle_img = fopen($gravatarimg, "wb+");
            fwrite($filehandle_img, $data);
            fclose($filehandle_img);
            unset($filehandle_img);
            unset($data);
        }
    }
}
closedir($handle);

Was macht dieses Script? Es aktualisiert alle 7 Tage die Gravatars, die bereits im Cache liegen. Auch dieses Script wurde auf curl umgestellt und ein bisschen aufgeräumt. Ich rate dazu, das Script wenigstens alle Stunde einmal ausführen zu lassen, um die Anzahl der gleichzeitig zu aktualisierenden Gravatare so gering wie möglich zu halten. Dies kann entweder per lokalem CronJob, oder einem CronJob-Dienst aus dem Internet wie beispielsweise cronjob.de ausgeführt werden (HowTo für letzteren Fall).

Meine letzten Hinweise zur Performance: Man sollte sich im klaren sein, dass bei größeren Kommentaranzahlen (sagen wir mal 300) auch bei jedem Kommentar geprüft wird, ob die Datei vorhanden ist (d.h. 300 Dateisystemaufrufe). Das kann definitiv bremsen.

Daher habe ich mir eine andere Methode der Auslieferung überlegt, welche ich in einem weiteren Artikel beschreiben werde.