You have two options, and most likely can combine them:
You can have a cron-job that runs every X-minutes, and runs over the database looking at each user and checks if they deserve a new badge.
For example, StackOverflow implements this for the Nice Answer badge. Everytime it runs, it checks how many Answers with +10 upvotes you have, and sees if you need to be awarded another badge. (It sees 5 posts with 10 upvotes and 4 Nice Answer Badges, you get a badge). Jeff has already stated that this means if you get an answer that gets a 10 vote, then downvoted, and then another post gets 10 votes, you won't get a badge.
The second option is event-based triggers. Which are really simply:
$badgeSystem->giveBadge("Some Badge Name", $User_ID);
This can be used for events you know are happening. Like the Autobiographer badge. Chances are the user won't fill out his profile unless the submit button is pressed, so the site could just check if the user has filled out the entire thing, and if they have, and they still need the badge, they get it.
The cron-job should be used for actions that are constantly checked. Things like quantitative goals like visiting the site for 150 days, or editing 500 times.
The event-based trigger should happen when an event will only happen if the user doesa specific action, such as submitting a form.
(You could probably use either for almost any situation. The event-based trigger gives quicker feedback though..)