elxsy where humanity wins the fight against machines

Documents

WordPress plugin file which includes an implementation of ImHuman api for cross php platform is already under downloads section. In this page I will mock up an client class to access the API, display and evaluate the humanizer results step by step with using PHP. You can use any language you wish to,including JAVASCRIPT.

Constants

We are going to define two constants for the whole class. First one is the API url of course and the second one will be out client library version. This version should match the properties of its API it works under. Current API + Client version is 1.0 and for future releases it can be found under API documentation.

<?php
# ImHuman Constants
DEFINE('IMHUMAN_API','http://api.elxsy.com/');
DEFINE('CLIENT_VERSION','1.0');

Class Structure

I am going to use API request variables as class properties also for extensible usage.Lets assume I will keep my settings in some configuration file and want to call my class with them every time I want to perform some security checks. So lets write our constructor function also.Don't worry I will put all this together in one file if you are already lost or do not want to spend time reading this.

Class ImHuman
{
	public $user;
	public $key;
	public $row;
	public $col;
	public $sel;
	private $rsp = NULL;

	public function __construct($user = NULL, $key = NULL, $row = 5, $col = 1, $sel = 3)
	{
		$this->user = $user;
		$this->key	= $key;
		$this->row	= $row;
		$this->col	= $col;
		$this->sel	= $sel;

		# we are using session to store our ultimate answer
		# if its not started already start it
		if(!session_id())
		{
			session_start();
		}
	}
}

Now we can send a request to the API and read its response. I am going to use file_get_contents() function. For more information please look at php.net website reference. You can use fopen(), fsocksopen(), curl() whatever floats your boat. This function simply open and reads the contents of a file to a variable. Since a website output is a file output also, we are going to use it.

Is this the best way ? hmmm no. Is it the practical way? Yup.

If function is not available in your hosting, please try to look for alternative methods as I mentioned. One of them should be allowed, if not than you should change that hosting :)

public function get()
{
	# lets prepare our request parameters
	$param = 'api='.CLIENT_VERSION
			.'&user='.urlencode($this->user)
			.'&key='.urlencode($this->key)
			.'&row='.urlencode($this->row)
			.'&col='.urlencode($this->col)
			.'&sel='.urlencode($this->sel);
	# send the request read and decode the response (as array)
	# and write it into class variable rsp
	$this->rsp = json_decode(@file_get_contents(IMHUMAN_API.'?'.$param),true);
}

So what have we done so far is, we prepared our parameters and send them to API by simply treating them as a file and directly read the response which is JSON encoded text. You can google what JSON is and if your PHP client does not support JSON encoding ( which comes automatically with PHP5) visit www.json.org and download the necessary JSON parser library to use or whichever JSON parser you like.

The last "true" parameter tells the decoder to associate the values to the variable, meaning $this->rsp is an array now instead of an object. If you do not use it or use it as false, results will return as an object.

So we may have succeed or failed through our quest who knows, well rsp variable knows because it has the API response. It may include errors but its not suitable to kill the application here or pop out a user error. Client does not know when or how you called itself, so best way to go through all this is to write a render function to display humanizers and display our user friendly error checking there. So here is our render function.

# We require an Unique ID here to prevent multiple instance chaos
# this can be a POST|PAGE ID in your website
# if your visitors doesnt open multiple instances of your security checks
# you are ok without it
public function render($UNIQUE_ID)
{
	# if ERROR CODE == 0, means no error occurred everything is smooth
	# For response details, please visit the API page
	if( !$this->rsp['ERROR'] )
	{
		# we are using session to store our ultimate answer
		# if its not started already start it
		if(!session_id())
		{
			session_start();
		}
		# Every ( well most of) PHP hostings are enabled by default (on Linux), dont worry
		# write the secret answer to the session with unique ID
		$_SESSION[$UNIQUE_ID.'ANSWER'] = $this->rsp['RSP']['ANSWER'];

		# Ask the visitor to select "Selected" Tag
		# I am using CSS styles to display the humanizers as table
		# you can use tables or anything u want
		echo '<div class="imhuman-title">Please Select : '.$this->rsp['RSP']['WORD'].'(s)</div>'
			.'<div class="imhuman-row pos-left clear-left" id="imhuman-div">';

		# Construct the grid
		for($i=0; $i < $this->row * $this->col; $i++)
		{
			# write the unique input names into session also
			# so we can find them when we evaluate them
			$_SESSION[$UNIQUE_ID.'KEYS'][] = $this->rsp['RSP']['GRID'][$i][1];

			# print each humanizer with its image, input name = value format
			echo '<div class="imhuman-item pos-left" style="background-image:url('.$this->rsp['RSP']['GRID'][$i][0].')" id="'
			.$this->rsp['RSP']['GRID'][$i][1].'">'
			.'<div class="imhuman-chk"><input type="checkbox" class="checkbox" name="'.$this->rsp['RSP']['GRID'][$i][1].'" value="'
			.$this->rsp['RSP']['GRID'][$i][2].'" /></div>'
			.'</div>';

			# just for display purposes, if colcount == given column make a new row in grid
			if( ($i + 1) % $this->col == 0 )
				echo '<div class="clear-left"></div>';
		}

		# close  the grid div and leave space clear for the next coming content
		echo '</div>'
			.'<div class="clear-left"></div>';
	}
	else
	{
		# well something went wrong and request failed, lets show the reason
		# and debug information

		echo '<div class="imhuman-error">'
			.'ImHuman Error Code : '.$this->rsp['ERROR'].'<br />'
			.'ImHuman Error Message : '.$this->rsp['RSP'].'<br />'
			.'ImHuman API VERSION :'.$this->rsp['APIV'].'<br />'
			.'ImHuman Client API Version : '.$this->rsp['CLAPIV'].'<br />'
			.'ImHuman Minumum Client API Version : '.$this->rsp['MAPIV'].'<br />'
			.'</div>';
	}
}

Well the code itself is commented with explanations so lets assume our class is ready to run in our website. Lets make a sample implementations and make it prettier to the human eye also. Dont forget to put the functions inside the class, I wrote them seperately for explanations. Save the file as ImHuman.class.php or Download the source from the link at the top or from downloads page.

Sample Implementation

Lets assume we have a news page and we have a comment form under it. So we want to humanly secure the comment form. Our visitor is browsing the page www.yourwebsite.com/news.php?id=101

Contens of news.php

<php
# blah blah blah php code
...
# this will be our unique ID
$UNIQUE = intval($_GET['id']);
...
require('ImHuman.class.php');

# fill in the values with your settings and desired values
# We construct the class before any content send to the browser due to session
# If you dont want to be limited start ur session somewhere else and remove the session code
$imhuman = new ImHuman(YOUR_API_USER,YOUR_API_KEY,HOW_MANY_ROWS,HOW_MANY_COLUMNS,HOW_MANY_TO_SELECT);

# make the request
$imhuman->get();
...
?>
<form action="post.php" method="post">
<label>Your Name</label>
<input type="text" name="your_name" >
<label>Your Message</label>
<textarea name="message"></textarea>
<?php
# Show the humanizers
$imhuman->render($UNIQUE);
?>
<input type="submit" value="Send" />
</form>

Now add this styles to your existing CSS file or include as a new one

Contents of imhuman.css

@charset "utf-8";
/* Yuksel Kurtbas - www.elxsy.com */
.imhuman-error {
	background:#FF7;
	padding:6px;
	border:1px dashed #F30;
	color:#000;
	font-weight:bold;
}
.imhuman-row {
	margin:4px 0;
}
.pos-left {
	float:left;
}
.clear-left {
	clear:left
}
.imhuman-title {
	padding:2px;
	font-weight:bold;
	width:auto;
	margin:0;
	color:#ccc;
}
.imhuman-item {
	background:#000;
	display:block;
	width:75px;
	height:75px;
	padding:6px;
	border:1px solid #333;
	background-position:center center;
	background-repeat:no-repeat;
	vertical-align:bottom;
	cursor:pointer;
}
.imhuman-chk {
	position:relative;
	display:block;
	float:right;
	width:13px;
	height:13px;
	margin-top:62px;
}
.imhuman-sel {
	border:1px solid #fff;
	background-color:#39C;
	opacity: 0.55;
	filter: alpha(opacity = 55);
}

Lets make selecting humanizers more stylish and fun. and lets hide the inputs if user's browser supports javascript. Also add this code in your JavaScript file or include new one. Position does not matter since both of them uses document onready event but preferably bottom of the page.

Contents of imhuman.js

/*
Yuksel Kurtbas - 2009 - www.elxsy.com
This is just pretty way of selecting humanizers.
They will work without javascript also.
*/
if (typeof(jQuery) != 'undefined' ) {
	//if jquery exists in the blog so lets write with that
	$(document).ready(function() {
		$('.imhuman-item').click(function(){
			e = $(this).hasClass('imhuman-sel');
			$("input[name='"+this.id+"']").attr('checked', !e);
			$(this).toggleClass('imhuman-sel',e);
		});
		$('.imhuman-chk').hide();
	});
} else {
	/* good old plain javascript for w3c compliant browsers..
	   haha of course IE is not included in them [smiley face]
	*/
    function humanize(){
		var divs = document.getElementById('imhuman-div').getElementsByTagName('DIV');
		for(i=0;i<divs.length;i++){
			if(divs[i].className=='imhuman-chk'){// hide checkboxes
				divs[i].style.display = 'none';
			} else if(divs[i].className=='imhuman-item pos-left'){
				divs[i].addEventListener('click',function () {
					c = document.getElementsByName(this.id)[0];
					e = this.className == 'imhuman-item pos-left' ? 1 : 0;
					c.checked = e;
					this.className = e ? 'imhuman-item pos-left imhuman-sel' : 'imhuman-item pos-left';
				},false);
			}
		}
	}
	document.addEventListener('DOMContentLoaded', humanize, false);
}

So finally, when you run the news.php in the sample I have provided, you should see this page

Sample shot of news.php

Sample shot of news.php

So last part is to check if the humanizers are selected correctly, meaning the visitor is a human and not spamming around his/her way. We will add a new function to our class for this. I am assuming you are using POST method, well you shouldn't use GET for this kinda of FORMS.

# We require an Unique ID here to know which ID to check again
public function check($UNIQUE_ID)
{
	# if API succeded and everything worked
	if(is_array($_SESSION[$UNIQUE_ID.'KEYS']))
	{
		# find the all hidden humanizers in the user post
		foreach($_SESSION[$UNIQUE_ID.'KEYS'] as $k)
		{
			# get them if user sent /selected them
			if( isset($_POST[$k]) )
			{
				$v[]=$_POST[$k];
			}
		}
		# and if user set any of them
		if(is_array($v))
		{
			sort($v);
			reset($v);
			# and finally if the user selected answer equal to the API given answer
			# delete old values and return true
			if ( md5(implode('.',$v)) == $_SESSION[$UNIQUE_ID.'ANSWER'] )
			{
				unset($_SESSION[$UNIQUE_ID.'ANSWER']);
				unset($_SESSION[$UNIQUE_ID.'KEYS']);
				return true;
			}
		}
	}
	# well if any of them is not true then its not true anyways = spam
	return false;
}

After adding this function to the Class, we can now construct post.php which will check if post values are true, not spam and insert into your made up database or do some other work

Contents of post.php

<php
# blah blah blah php code
...
$UNIQUE = intval($_POST['id']);

require('ImHuman.class.php');

# it is empty because we are only going to use it
# to check to see if post is spam
$imhuman = new ImHuman();

# make the check and print error in your own way
if( !$imhuman->check( $UNIQUE ) )
{
	your_custom_error_function('Please select correct images');
}
...

So thats finally it. Now you can use this class or create your own code for ImHuman API usage.

If you have any questions or updates to the documentation, please drop a comment and I will update it.

Comments (4) Trackbacks (0)
  1. How do I translate the api response to my language?

    • Hello nivosh,

      You can translate the tags – in the “Add Humanizers” menu – into your own language and email them to me, then if you are using wordpress plugin with a little setting, your imHuman will work in your language.

  2. Hi, I love everything you have done BTW. I may have missed it, but do you have a version that works with wordpress Version 2.9.1?


Leave a comment

No trackbacks yet.