tags:

views:

670

answers:

8

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('[email protected]') 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.

+1  A: 

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 = "[email protected]";
*        $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 <[email protected]>
* @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 = '[email protected]';
$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
I'd recommend modifying the class to replace the deprecated (since 5.3) call to eregi and using preg_match with /i
hobodave
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
A: 

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 C.
I wish it was consistent, but it's different for every email address, whether it's a valid gravatar or not. :(
Jeff
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
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
A: 

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('[email protected]'));
Philippe Gerber
I don't think the folks at Gravatar would be overly enthused about this approach. ;-)
scunliffe
Then they should provide an API ... ;-)
Philippe Gerber
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
A: 

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&amp;d=identicon&amp;r=PG"
  onload="itWorked();"
  onerror="itFailed();"/>
scunliffe
I don't think that'll work because the image never fails... Always a 200 response.
Jeff
Ah, bummer. Guess it pays to read the question thoroughly.
scunliffe
+2  A: 

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.

(Don't know why I try to help, as I don't like avatar sites to start with...)

Arjan
Jeff
The *first* GET parameter must always be prefixed with a question mark; all subsequent parameters are separated using the ampersand.
Arjan
A: 

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&amp;size=310';

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

Jason
not sure why this is modded down, i have this working perfectly in a production environment.
Jason
I'm not sure either, possibly because the original question mentions the default parameter already. For karma and the help, voted up.
Jeff
ah, missed that....
Jason
+1  A: 

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

Andrew Aylett