A Simple Stack Overflow-Style Badge Framework for PHP

badgesI'm pretty infatuated with Stack Overflow's badge system, so I decided to cook up a generic framework for adding one to my crossword site. Herein I will present my work in progress for the consumption of my dear readers.

The storage of these badge-awarding rules seemed interesting to me. It'd have to be flexible enough to take data from any part of the site related to a user, and not be a hassle to add new badges or check a user's list of acquired badges. I haven't put the caching in yet, which would form the basis for making awarded badges permanent, instead of calculated on the fly, but it's still pretty fun to work with.

I started off by creating a simple Badge interface.

Badge.php

interface Badge {
  public function getActive();
  public function validFor($user);
  public function toString();
  public function getColor();
}

Then tossed in a little abstract class to handle some defaults:

AbstractBadge.php

abstract class AbstractBadge implements Badge {
  public function getColor() {
    return "#cccccc";
  }

  public function getActive() {
    return true;
  }

  public function toString() {
    return "Default";
  }
}

Now I needed a way to go through these badges and find out which applied to a User object. I wanted to use a plugin style system where I'd just need to have a directory full of these Badge implementors and it would figure out which ones to apply. This bit is certainly in-progress, but works well enough to demonstrate.

Badges.php

define('BADGE_DIR',Zend_Registry::get('config')->badge->path);

class Badges extends ArrayObject {
  public function __construct() {
    $badgedir = opendir(BADGE_DIR);
    while ($f = readdir($badgedir)) {
      if (preg_match('/Badge\.php$/',$f)) {
        try {
          include BADGE_DIR.'/'.$f;
          $cls = str_replace('.php','',$f);
          $badge = new $cls;
   
          if ($badge instanceof Badge) {
            if ($badge->getActive())
              $this->append($badge);
          }
        } catch (Exception $ex) {}
      }
    }
    return $this;
  }

  public function getUserBadges($user) {
    $result = new ArrayObject();
    foreach ($this as $badge) {
      if ($badge->validFor($user))
        $result->append($badge);
    }
    return $result;
  }
}

This iterates through the Badges in a directory (one defined by a path in my site config file, in this case), and returns all active badges for a certain user. Now it's fairly trivial to add new badges.

For example, we could have a badge that would reward the user for being me.

AdamBadge.php

class AdamBadge extends AbstractBadge {
  public function validFor($user) {
    return $user->username == 'adam';
  }

  public function toString() {
    return "Your name is Adam";
  }
}

And now I'll be congratulated for being myself every time I check my user profile. What a fine feeling it is.

2 thoughts on “A Simple Stack Overflow-Style Badge Framework for PHP”

  1. Up next:

    “class AdamBadge extends AbstractBadge {
    public function validFor($user) {
    return $user->username == ‘gibzon’;
    }

    public function toString() {
    return “You are awesome!”;
    }
    }”

Leave a Reply

Your email address will not be published. Required fields are marked *