5

I'm pretty new to PHP, so if you have any thoughts or suggestions to point me in the right direction, I'd be grateful.

Trying to make a simple function to check if a user's email address translates into a valid Gravatar Image, but it seems gravatar.com has changed their headers.

Using get_headers('urlencoded_bad_email@example.com') returns a 200 instead of 302.

Here are the headers from a bad gravatar image, none of which seem to be able to help because they are identical to a valid gravatar image:

array(13) {
  [0]=>
  string(15) "HTTP/1.1 200 OK"
  [1]=>
  string(13) "Server: nginx"
  [2]=>
  string(35) "Date: Sun, 26 Jul 2009 20:22:07 GMT"
  [3]=>
  string(24) "Content-Type: image/jpeg"
  [4]=>
  string(17) "Connection: close"
  [5]=>
  string(44) "Last-Modified: Sun, 26 Jul 2009 19:47:12 GMT"
  [6]=>
  string(76) "Content-Disposition: inline; filename="5ed352b75af7175464e354f6651c6e9e.jpg""
  [7]=>
  string(20) "Content-Length: 3875"
  [8]=>
  string(32) "X-Varnish: 3883194649 3880834433"
  [9]=>
  string(16) "Via: 1.1 varnish"
  [10]=>
  string(38) "Expires: Sun, 26 Jul 2009 20:27:07 GMT"
  [11]=>
  string(26) "Cache-Control: max-age=300"
  [12]=>
  string(16) "Source-Age: 1322"
}

p.s. I am aware of the '&d' parameter, but it will not serve my purpose. :)

EDIT:

Use '?d' instead of '&d'. Must be a gravatar.com 'thang.

Jeff
  • 5,962
  • 16
  • 49
  • 81
  • 5
    (Just a side note: as per RFC2607, in documentation please always use @example.com, @example.org or @example.net -- no need to have the people at address.com get your spam.) – Arjan Jul 26 '09 at 21:07
  • Aaah! I KNEW that, too. LOL, fixed. – Jeff Jul 26 '09 at 22:06

8 Answers8

6

Gravatar have added an option to the 'd' parameter, meaning that if you pass in d=404, you get a 404 page (instead of some 302 redirect to a default picture) if there's no picture, rather than having to use heuristics.

Arjan
  • 22,808
  • 11
  • 61
  • 71
Andrew Aylett
  • 39,182
  • 5
  • 68
  • 95
4

NOTE: at the time of writing, this was the only option. However, some later time ?d=404 was added, making Andrew's answer much cleaner.


Though you said you know about the d parameter, do you know it actually returns a redirect header when applicable? So, the following yields 302 Found because the avatar does not exist:

http://www.gravatar.com/avatar/3b3be63a4c2a439b013787725dfce802?d=http%3A%2F%2Fwww.google.com%2Fimages%2Flogo.gif

HTTP/1.1 302 Found  
...  
Last-Modified: Wed, 11 Jan 1984 08:00:00 GMT  
Location: http://www.google.com/images/logo.gif  
Content-Length: 0  
...  
Expires: Sun, 26 Jul 2009 23:18:33 GMT  
Cache-Control: max-age=300

Seems to me that all you need to do is add that d parameter and check the HTTP result code then.

Community
  • 1
  • 1
Arjan
  • 22,808
  • 11
  • 61
  • 71
  • 1
    This works. It appears the determining factor is using the '?d=' versus using the '&d=' for the default gravatar. – Jeff Jul 27 '09 at 13:09
  • 2
    The *first* GET parameter must always be prefixed with a question mark; all subsequent parameters are separated using the ampersand. – Arjan Jul 27 '09 at 14:05
  • Interpreting a 302 status code as a failure is very fishy. Just use the 404 default, and you're set. – cweiske Aug 04 '12 at 18:25
  • @cweiske, that would be true if Gravatar would return 404s. But [they don't](http://web-sniffer.net/?url=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fno-such-hash-exists&submit=Submit&http=1.1&type=GET&uak=0). So how could one act on a 404 that is not returned? – Arjan Aug 04 '12 at 18:45
  • Ah, I see, `?d=` since some time also allows for simply `?d=404`. @Jeff, can you accept Andrew's answer so I can delete this one? – Arjan Aug 04 '12 at 18:50
2

I suggest you try the php gravatar class by Lucas Araújo.

/**
*  Class Gravatar
*
* From Gravatar Help:
*        "A gravatar is a dynamic image resource that is requested from our server. The request
*        URL is presented here, broken into its segments."
* Source:
*    http://site.gravatar.com/site/implement
*
* Usage:
* <code>
*        $email = "youremail@yourhost.com";
*        $default = "http://www.yourhost.com/default_image.jpg";    // Optional
*        $gravatar = new Gravatar($email, $default);
*        $gravatar->size = 80;
*        $gravatar->rating = "G";
*        $gravatar->border = "FF0000";
*
*        echo $gravatar; // Or echo $gravatar->toHTML();
* </code>
*
*    Class Page: http://www.phpclasses.org/browse/package/4227.html
*
* @author Lucas Araújo <araujo.lucas@gmail.com>
* @version 1.0
* @package Gravatar
*/
class Gravatar
{
    /**
     *    Gravatar's url
     */
    const GRAVATAR_URL = "http://www.gravatar.com/avatar.php";

    /**
     *    Ratings available
     */
    private $GRAVATAR_RATING = array("G", "PG", "R", "X");

    /**
     *    Query string. key/value
     */
    protected $properties = array(
        "gravatar_id"    => NULL,
        "default"        => NULL,
        "size"            => 80,        // The default value
        "rating"        => NULL,
        "border"        => NULL,
    );

    /**
     *    E-mail. This will be converted to md5($email)
     */
    protected $email = "";

    /**
     *    Extra attributes to the IMG tag like ALT, CLASS, STYLE...
     */
    protected $extra = "";

    /**
     *    
     */
    public function __construct($email=NULL, $default=NULL) {
        $this->setEmail($email);
        $this->setDefault($default);
    }

    /**
     *    
     */
    public function setEmail($email) {
        if ($this->isValidEmail($email)) {
            $this->email = $email;
            $this->properties['gravatar_id'] = md5(strtolower($this->email));
            return true;
        }
        return false;
    }

    /**
     *    
     */
    public function setDefault($default) {
        $this->properties['default'] = $default;
    }

    /**
     *    
     */
    public function setRating($rating) {
        if (in_array($rating, $this->GRAVATAR_RATING)) {
            $this->properties['rating'] = $rating;
            return true;
        }
        return false;
    }

    /**
     *    
     */
    public function setSize($size) {
        $size = (int) $size;
        if ($size <= 0)
            $size = NULL;        // Use the default size
        $this->properties['size'] = $size;
    }

    /**
     *    
     */
    public function setExtra($extra) {
        $this->extra = $extra;
    }

    /**
     *    
     */
    public function isValidEmail($email) {
        // Source: http://www.zend.com/zend/spotlight/ev12apr.php
        return eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $email);
    }

    /**
     *    Object property overloading
     */
    public function __get($var) { return @$this->properties[$var]; }

    /**
     *    Object property overloading
     */
    public function __set($var, $value) {
        switch($var) {
            case "email":    return $this->setEmail($value);
            case "rating":    return $this->setRating($value);
            case "default":    return $this->setDefault($value);
            case "size":    return $this->setSize($value);
            // Cannot set gravatar_id
            case "gravatar_id": return;
        }
        return @$this->properties[$var] = $value;
    }

    /**
     *    Object property overloading
     */
    public function __isset($var) { return isset($this->properties[$var]); }

    /**
     *    Object property overloading
     */
    public function __unset($var) { return @$this->properties[$var] == NULL; }

    /**
     *    Get source
     */
    public function getSrc() {
        $url = self::GRAVATAR_URL ."?";
        $first = true;
        foreach($this->properties as $key => $value) {
            if (isset($value)) {
                if (!$first)
                    $url .= "&";
                $url .= $key."=".urlencode($value);
                $first = false;
            }
        }
        return $url;    
    }

    /**
     *    toHTML
     */
    public function toHTML() {
        return     '<img src="'. $this->getSrc() .'"'
                .(!isset($this->size) ? "" : ' width="'.$this->size.'" height="'.$this->size.'"')
                .$this->extra
                .' />';    
    }

    /**
     *    toString
     */
    public function __toString() { return $this->toHTML(); }
} 

and this is how you'd use it:

include 'gravatar.php';
$eMail = 'name@email.net';
$defImg = 'http://www.example.com/images/myphoto.jpg';
$avatar = new Gravatar($eMail, $defImg);
$avatar->setSize(90);
$avatar->setRating('G');
$avatar->setExtra('alt="my gravatar"');

<p>
<?php echo $avatar->toHTML(); ?>
</p>
pixeline
  • 17,669
  • 12
  • 84
  • 109
  • 1
    I'd recommend modifying the class to replace the deprecated (since 5.3) call to eregi and using preg_match with /i – hobodave Jul 26 '09 at 21:01
  • The reply is much appreciated, but I only need a function to check for a valid gravatar image. If a valid image is not found, the function needs to return FALSE. Unless I overlooked it, I did not see code in the above class for that. – Jeff Jul 26 '09 at 21:03
1

Extending the answer by Andrew Aylett about d=404, actually it's possible to compose a Gravatar query with d=404 (or default=404), then look into headers if key [0] contains a value 404 or 200.

$email = md5(strtolower("myemailaddress@example.com"));
$gravatar = "http://www.gravatar.com/avatar/$email?d=404";
$headers = get_headers($gravatar,1);
if (strpos($headers[0],'200')) echo "<img src='$gravatar'>"; // OK
else if (strpos($headers[0],'404')) echo "No Gravatar"; // Not Found

Original question dates back three years ago. Maybe at that time Gravatar's headers stuff was slightly different.

Salvador
  • 786
  • 8
  • 21
1

add the "default" parameter to the image url when checking for a gravatar, this will provide a 302 redirect if the image is not found.

$grav_url = 'http://www.gravatar.com/avatar/'.md5(mb_strtolower($email)).'?default=http://www.mysite.com/null.jpg&size=310';

the null image could then return a 404 if you want it to :)

Jason
  • 2,035
  • 11
  • 13
  • not sure why this is modded down, i have this working perfectly in a production environment. – Jason Jul 27 '09 at 11:48
  • I'm not sure either, possibly because the original question mentions the default parameter already. For karma and the help, voted up. – Jeff Jul 27 '09 at 13:10
  • Or, as I posted the very same answer a day earlier? Anyway, this has meanwhile been obsoleted by Andrew's answer, so I guess both mine and yours can be deleted now! – Arjan Aug 04 '12 at 18:53
0

Is the filename ( Content-Disposition: inline; filename="5ed352b75af7175464e354f6651c6e9e.jpg" ) consistent for "not found/invalid" Gravatar images? If so, you could use that to identify invalid images?

Richy B.
  • 1,619
  • 12
  • 20
  • I wish it was consistent, but it's different for every email address, whether it's a valid gravatar or not. :( – Jeff Jul 26 '09 at 21:20
  • Is the content the same for invalid email addresses even if the file name is not? Maybe you could take the MD5 hash of a known "invalid" response and use that to compare... – Ken Keenan Jul 26 '09 at 21:26
  • Yes, the content is the same for invalid email addresses even though the file name is not. The problem is, the valid responses are identical to the invalid responses. – Jeff Jul 26 '09 at 22:08
0

Not sure exactly how you want to use this info once you get it... but could you:

Load the image on a webpage, with an onload or onerror handler attached... if the onload fires, you have a match, if the onerror fires it either doesn't exist (or there are issues loading it)

e.g.

<img
  src="http://www.gravatar.com/avatar/282eed17fcb9682bb2816697482b64ec?s=128&d=identicon&r=PG"
  onload="itWorked();"
  onerror="itFailed();"/>
scunliffe
  • 62,582
  • 25
  • 126
  • 161
-1

A really unperformant solution could be to post email to http://en.gravatar.com/accounts/signup and check for Sorry, that email address is already used! ...

edit

Okay, they use some cookie to indicate if an error occured or not ... ;-)

function isUsed($email)
{
    $url   = 'http://en.gravatar.com/accounts/signup';
    $email = strtolower($email);

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, 'commit=Signup&email=' . urlencode($email));
    curl_setopt($ch, CURLOPT_HEADER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    return (false !== strpos($response, 'Set-Cookie: gravatar-notices'));
}

var_dump(isUsed('test@example.org'));
Philippe Gerber
  • 17,457
  • 6
  • 45
  • 40
  • 3
    I don't think the folks at Gravatar would be overly enthused about this approach. ;-) – scunliffe Jul 26 '09 at 21:56
  • Then they should provide an API ... ;-) – Philippe Gerber Jul 26 '09 at 22:01
  • 1
    re: the API; I agree wholeheartedly. They recently changed the 302's on an invalid gravatar to a 200... likely for this exact purpose. Why wouldn't they allow webmasters to check for an invalid image? Makes no sense. – Jeff Jul 26 '09 at 22:11