Unstoppable Domains

An in-depth approach to a fair PHP ad randomizer [by Legend2]

Spacemail by SpaceshipSpacemail by Spaceship
Watch
Impact
22
An in-depth approach to a fair PHP ad randomizer
By legend2


The purpose of this tutorial is to shed some light on a way to improve php randomizers.
Suppose you got a heavy-traffic website, but a single banner ad spot.
Suppose too that you want to allow multiple advertizers to 'share' this space.

The biggest question an advertizer would ask here: will my ad get the same share of impressions as others showing on the same spot?

The fact is, using random functions to select an arbitrary 'include' is not that fair. No matter what the seed is, and depending on the size/number of possible ads to

include, some ad may be favorized more than others.

Our aim here is to divide the number of impressions equally among the advertized ads.


To give a clear idea of what I'm talking about here, we visualize teh following scenario:

We have an '/ads' folder under the root web directory of the website where the ads should show.
This folder will contain multiple files, each file correspoding to a different ad.

We also assume the web server is running PHP, and a MySQL DB.

We start off by creating a table called 'site_ads'. This table will contain two fields: 1) an auto increment field called 'id' which is the primary key for this table. This

field will uniquely identify each of the ads. 2) the file name corresponding to the ad, which can be found in the /ads directory. We'll call this field 'name'.

The query for this looks as follows:

Code:
CREATE TABLE site_ads (
id INT NOT NULL AUTO_INCREMENT ,
name VARCHAR( 100 ) NOT NULL ,
PRIMARY KEY (id)
)

Now, from now on, whenever you want to add a new ad to the rotation, simply insert a new entry in this table, and add its corresponding file in the /ads folder.
An example of such a query would be:

Code:
insert into site_ads('',"site1");

The corresponding file in the ads/ folder should then be called site1.php (note that we use a .php extension for the ad files, while the actual content can be static, such

as an <img>, but .php used as an example, in case you might need to add custom processing for every ad at an advanced stage).

We now reach our key idea: we do have the ads placed, and their corresponding entries in the database. How to divide impressions equally among them?

We create a new table in the database called 'counter'. The main purpose of this table is to keep track of the last ad viewed. This will help determining the next ad to

display. This table has one field, we call 'current' which will serve the purpose just stated. Note that if 'current' has a value of '5' it means fifth ad, and not the ad

with 'id' 5.
Why? The reason is that at some point, old ads will be removed, and others will be added. This will result in the 'id' field which is automatically generated (because of

its auto increment nature) having 'gaps'. Our approach will take care of this point.

The query for our new table is:

Code:
CREATE TABLE counter (
counter INT NOT NULL,
PRIMARY KEY (counter)
)

If this is the first time we run the randomizer, we would insert the value '0' which will correspond to the first 'id' the auto increment field of the site_ads table will

generate.

Such a query is:

Code:
insert into counter values('0');

We will follow a cyclic loop over all the ads. This means that once the counter reaches the last ad in the table, it will start off again from the beginning.


The following is a detailed PHP code which will illustrate the approch we are following (This is the code to be used in the actual ad space):

PHP:
<?

$dbname = "database_name";
$databaseuser = "username";
$databasepass = "password";
$adsfolder = "ads/";

mysql_connect(localhost,$databaseuser,$databasepass) or die("couldn't connect");

mysql_select_db($dbname) or die("couldn't select database");

$counter_r = mysql_query("select * from counter");

$counter_r2 = mysql_fetch_array($counter_r);

$counter = $counter_r2[0];

if(!isset($counter)) {
	mysql_query("insert into counter values('0')");
	$counter = -1;
	$total = 1;
}

$total_r = mysql_query("select * from site_ads");

$total_r2 = mysql_num_rows($total_r);

if($total_r2>0)
	$total = $total_r2;

$newcounter = ($counter+1)%($total);

$ad_r = mysql_query("select * from site_ads limit $newcounter,1");

$ad_r2 = mysql_fetch_array($ad_r);

$ad = $ad_r2['name'];

mysql_query("update table counter set current = '$newcounter'");

mysql_close();

include $adsfolder . $ad;

?>

The steps followed in the above code are:
- connect to the database
- fetch the current counter
- if counter is not initialized, initialize it to -1 for later increments to 0, and insert it in the database
- fetch the total number of ads in the database
- set total to 1 in case none exists yet
- get the next ad counter by incrementing the current one by 1 and using the math 'modulo %' to limit it to the number of total ads and give it a cyclic nature
- select the newcounter'th ad entry
- update the new counter location
- display the ad file.

The above code can be saved in a separate file called ads.php, and we can just include it from another php file.

PHP:
<? include "ads.php" ?>

Place the above line wherever the ad rotation usually should show.

Good Luck. (The code provided in this tutorial has not been tested, feel free to post comments in case you find any bug).

Legend2, All rights reserved.
 
Last edited:
1
•••
The views expressed on this page by users and staff are their own, not those of NamePros.
.US domains.US domains
Nice Post.

If you have mysql 4 or 4.1 I am not 100% sure which one you can always use the RAND command
PHP:
SELECT ad,id FROM site_ads ORDER BY RAND(NOW()) LIMIT 1;

Or if you want EVEN fast processing

PHP:
LOCK TABLES site_ads READ;
SELECT FLOOR(RAND() * COUNT(*)) AS rand_row FROM site_ads;
SELECT * FROM site_ads LIMIT $rand_row, 1;
UNLOCK TABLES;

These are just mysql querys you need to put them into PHP to make them work.
iNod.
 
0
•••
any other comments folks?
 
0
•••
Nice post legend. Ad rotation is so simple, yet some people find so hard to do. I find that alot of folks will use something like this:

PHP:
<?php

srand((double) microtime * 100000);

$ad = rand(1, 5);

switch($ad)
{
  case 1:
    $banner_code = "<a href=\"site.com\"><img src=\"images/banner1.gif\" border=\"0\" width=\"468\" height=\"60\"></a>";
    break;
  case 2:
    $banner_code = "<a href=\"site.com\"><img src=\"images/banner2.gif\" border=\"0\" width=\"468\" height=\"60\"></a>";
    break;
  case 3:
    $banner_code = "<a href=\"site.com\"><img src=\"images/banner3.gif\" border=\"0\" width=\"468\" height=\"60\"></a>";
    break;
  case 4:
    $banner_code = "<a href=\"site.com\"><img src=\"images/banner4.gif\" border=\"0\" width=\"468\" height=\"60\"></a>";
    break;
  case 5:
    $banner_code = "<a href=\"site.com\"><img src=\"images/banner5.gif\" border=\"0\" width=\"468\" height=\"60\"></a>";
    break;
}

echo $banner_code;

?>

Which is definitely not a fair or accurate ad rotation system. Something like what you've written would def. work much better legend. Again, nice post. Rep added. :)

-Eric
 
Last edited:
0
•••
Thanks Eric :blush:
 
0
•••
Before I post my input, may I ask, is rand(0, 4) fair for 0-4?
 
0
•••
fair would be, if the script is called x times and you have y ads, then each ad would receive floor of x/y.
the rand function itself, might conceptually throw x times the same number. and it is more likely to favorise a number on another as the number it can pick from are narrow.
in my example, i force the choosing of each ad in a fair fashion.
so i a user loads the page at second a displaying ad number 1 then the next user, regardeless of the time frame, will see ad number 2.
 
0
•••
Appraise.net
Domain Recover
DomainEasy โ€” Live Options
  • The sidebar remains visible by scrolling at a speed relative to the pageโ€™s height.
Back