├── README ├── phpab.php └── samples ├── php-logic └── index.php └── phpab-tags └── index.php /README: -------------------------------------------------------------------------------- 1 | License: http://www.gnu.org/licenses/gpl-2.0.html 2 | -------------------------------------------------------------------------------- /phpab.php: -------------------------------------------------------------------------------- 1 | detect_bots == TRUE) 31 | { 32 | $bots = array('googlebot', 'msnbot', 'slurp', 'ask jeeves', 'crawl', 'ia_archiver', 'lycos'); 33 | foreach($bots as $botname) 34 | { 35 | if(stripos($_SERVER['HTTP_USER_AGENT'], $botname) !== FALSE) 36 | { 37 | $this->trial_mode = TRUE; 38 | $this->is_bot = TRUE; 39 | break; 40 | } 41 | } 42 | } 43 | 44 | if($this->is_bot == FALSE) 45 | { 46 | $this->trial_mode = $t; 47 | } 48 | 49 | ob_start(array($this, 'execute')); 50 | 51 | $this->test_domain = '.' . $_SERVER['HTTP_HOST']; 52 | 53 | $n = trim(strtolower($n)); 54 | $n = preg_replace('/[^a-z0-9 _]*/', '', $n); 55 | $n = str_replace(' ', '_', $n); 56 | $this->test_name = $n; 57 | } 58 | 59 | function __destruct () 60 | { 61 | ob_end_flush(); 62 | } 63 | 64 | private function grab_content () 65 | { 66 | if(empty($this->content)) 67 | { 68 | $this->content = ob_get_contents(); 69 | } 70 | } 71 | 72 | private function setup_ga () 73 | { 74 | $try_auto = FALSE; 75 | $sync = '{' . $this->tag . ' ' . $this->test_name . ' ga_sync}'; 76 | $async = '{' . $this->tag . ' ' . $this->test_name . ' ga_async}'; 77 | $syncPos = strpos($this->content, $sync); 78 | if($syncPos !== FALSE) 79 | { 80 | $this->content = str_replace($sync, 'pageTracker._setCustomVar(' . $this->ga_slot . ', "' . $this->test_name . '", "' . $this->current_variation . '", 3);', $this->content); 81 | } 82 | else 83 | { 84 | $asyncPos = strpos($this->content, $async); 85 | if($asyncPos !== FALSE) 86 | { 87 | $this->content = str_replace($async, '_gaq.push(["_setCustomVar", ' . $this->ga_slot . ', "' . $this->test_name . '", "' . $this->current_variation . '", 3]);', $this->content); 88 | } 89 | else 90 | { 91 | $try_auto = TRUE; 92 | } 93 | } 94 | 95 | if($this->auto_ga == TRUE && $try_auto == TRUE) 96 | { 97 | $sync = strpos($this->content, 'pageTracker._trackPageview'); 98 | if($sync === FALSE) 99 | { 100 | $async = preg_match('/_gaq\.push\(\[[\'\"]_trackPageview[\'\"]\]\)/', $this->content, $matches, PREG_OFFSET_CAPTURE); 101 | if($async == FALSE) 102 | { 103 | $auto_fail = TRUE; 104 | $async = FALSE; 105 | } 106 | else 107 | { 108 | $auto_fail = FALSE; 109 | $async = $matches[0][1]; 110 | } 111 | } 112 | else 113 | { 114 | $auto_fail = FALSE; 115 | } 116 | 117 | if($auto_fail === FALSE && $sync !== FALSE) 118 | { 119 | $this->content = substr($this->content, 0, $sync - 1) . 'pageTracker._setCustomVar(' . $this->ga_slot . ', "' . $this->test_name . '", "' . $this->current_variation . '", 3);' . substr($this->content, $sync); 120 | } 121 | elseif($auto_fail === FALSE && $async !== FALSE) 122 | { 123 | $this->content = substr($this->content, 0, $async - 1) . ' _gaq.push(["_setCustomVar", ' . $this->ga_slot . ', "' . $this->test_name . '", "' . $this->current_variation . '", 3]); ' . substr($this->content, $async); 124 | } 125 | } 126 | } 127 | 128 | private function record_user_segment () 129 | { 130 | $cookie_domain = (($colon_position = strrpos($this->test_domain, ":")) === false) ? $this->test_domain : substr($this->test_domain, 0, $colon_position); 131 | setcookie($this->tag . '-' . $this->test_name, $this->current_variation, time() + (60 * 60 * 24 * 365), '/', $cookie_domain); 132 | } 133 | 134 | public function set_domain ($d) 135 | { 136 | $this->test_domain = !empty($d) ? $d : '.' . $_SERVER['HTTP_HOST']; 137 | } 138 | 139 | public function set_ga_slot ($s) 140 | { 141 | $this->ga_slot = $s; 142 | } 143 | 144 | public function set_ga_mode ($m) 145 | { 146 | $this->ga_auto = $m; 147 | } 148 | 149 | public function set_tag ($t) 150 | { 151 | $this->tag = $t; 152 | } 153 | 154 | public function add_variation ($n, $v = '') 155 | { 156 | $n = trim(strtolower($n)); 157 | $n = preg_replace('/[^a-z0-9 _]*/', '', $n); 158 | $n = str_replace(' ', '_', $n); 159 | 160 | array_push($this->variations, array('name' => $n, 'value' => $v)); 161 | } 162 | 163 | public function get_user_segment () 164 | { 165 | if($this->current_variation != '!unset' && $this->current_variation_key != -1) 166 | { 167 | return $this->current_variation; 168 | } 169 | 170 | if ($this->is_bot == TRUE) 171 | { 172 | $this->current_variation = 'control'; 173 | return $this->current_variation; 174 | } 175 | 176 | if (get_magic_quotes_gpc() == TRUE) 177 | { 178 | $_COOKIE[$this->tag . '-' . $this->test_name] = stripslashes($_COOKIE[$this->tag . '-' . $this->test_name]); 179 | } 180 | 181 | if($this->trial_mode == FALSE) 182 | { 183 | $key = $this->tag . '-' . $this->test_name; 184 | if(array_key_exists($key, $_COOKIE)) 185 | { 186 | $this->current_variation = $_COOKIE[$key]; 187 | } 188 | 189 | if(empty($this->current_variation)) 190 | { 191 | $this->current_variation = '!unset'; 192 | } 193 | } 194 | else 195 | { 196 | $this->current_variation = '!unset'; 197 | } 198 | 199 | array_unshift($this->variations, array('name' => 'control', 'value' => '')); 200 | 201 | $valid = FALSE; 202 | 203 | $this->current_variation_key = 0; 204 | foreach($this->variations as $n => $v) 205 | { 206 | if($v['name'] == $this->current_variation) 207 | { 208 | $valid = TRUE; 209 | break; 210 | } 211 | $this->current_variation_key++; 212 | } 213 | 214 | if($this->current_variation == '!unset' || $valid == FALSE) 215 | { 216 | srand((double)microtime() * 1000003); 217 | $this->current_variation_key = array_rand($this->variations); 218 | $this->current_variation = $this->variations[$this->current_variation_key]['name']; 219 | } 220 | 221 | return $this->current_variation; 222 | } 223 | 224 | public function execute ($buffer) 225 | { 226 | $this->content = $buffer; 227 | 228 | if($this->test_ran == FALSE) 229 | { 230 | $this->run_test(); 231 | } 232 | 233 | if($this->trial_mode != TRUE) 234 | { 235 | $this->setup_ga(); 236 | } 237 | 238 | $tmp = $this->content; 239 | $this->content = preg_replace('/]*?)class="([^"]*?)"([^>]*?)>/i', 'tag . '-' . $this->current_variation . '"${3}>', $this->content); 240 | if($tmp == $this->content) 241 | { 242 | $this->content = preg_replace('/]*?)>/i', '', $this->content); 243 | } 244 | unset($tmp); 245 | 246 | $pos = strrpos($this->content, ''); 247 | if($pos !== false) 248 | { 249 | $this->content = substr_replace($this->content, '', $pos, strlen('')); 250 | } 251 | 252 | $this->content = str_replace('{' . $this->tag . ' ' . $this->test_name . ' current_varation}', $this->current_variation, $this->content); 253 | 254 | if($this->trial_mode != TRUE) 255 | { 256 | $this->record_user_segment(); 257 | } 258 | 259 | return $this->content; 260 | } 261 | 262 | public function run_test () 263 | { 264 | $this->get_user_segment(); 265 | $this->grab_content(); 266 | 267 | $open_tag = '{' . $this->tag . ' ' . $this->test_name . '}'; 268 | $close_tag = '{/' . $this->tag . ' ' . $this->test_name . '}'; 269 | $test_open = strpos($this->content, $open_tag); 270 | $test_close = strpos($this->content, $close_tag); 271 | 272 | while($test_open !== FALSE) 273 | { 274 | if($this->current_variation != 'control') 275 | { 276 | if($test_close === FALSE && $test_open !== FALSE) 277 | { 278 | $this->content = substr_replace($this->content, $this->variations[$this->current_variation_key]['value'], $test_open, strlen($open_tag)); 279 | } 280 | elseif($test_close !== FALSE && $test_open !== FALSE) 281 | { 282 | $diff = $test_close + strlen($close_tag) - $test_open; 283 | $this->content = substr_replace($this->content, $this->variations[$this->current_variation_key]['value'], $test_open, $diff); 284 | } 285 | else 286 | { 287 | } 288 | } 289 | else 290 | { 291 | $this->content = str_replace($open_tag, $this->variations[$this->current_variation_key]['value'], $this->content); 292 | $this->content = str_replace($close_tag, '', $this->content); 293 | } 294 | 295 | $test_open = strpos($this->content, $open_tag, $test_open); 296 | $test_close = strpos($this->content, $close_tag, $test_open); 297 | } 298 | 299 | $this->test_ran = TRUE; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /samples/php-logic/index.php: -------------------------------------------------------------------------------- 1 | add_variation('logos'); 6 | $my_test->add_variation('ethos'); 7 | $my_test->add_variation('pathos'); 8 | 9 | ?> 10 | 11 | 12 | Saving fluffy bunnies, one A/B test at a time 13 | 14 | 32 | 33 | 43 | 44 | 45 | 46 | get_user_segment() == 'logos') : ?> 47 |

99% of people are concerned about fluffy bunnies.

48 | get_user_segment() == 'ethos') : ?> 49 |

Fluffy bunnies deserve to be protected.

50 | get_user_segment() == 'pathos') : ?> 51 |

Fluffy bunnies are cute and snuggly.

52 | 53 |

OMG! Fluffy bunnies!

54 | 55 | 56 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sapien orci, tincidunt nec euismod sit amet, porttitor sed massa. Vestibulum sollicitudin risus eu quam consequat elementum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vitae nunc eget tellus semper rhoncus imperdiet ac arcu. Suspendisse et felis lacus. Suspendisse neque dui, suscipit et vehicula at, lacinia at leo. Pellentesque at dignissim leo. Phasellus felis lectus, varius sit amet sodales vel, convallis sit amet leo. Curabitur mi sapien, tristique ac rutrum at, posuere non dolor. Sed non fermentum nisi. Vivamus sapien nisi, blandit at imperdiet vitae, tempus eget leo. Phasellus ultrices interdum pretium. Nunc ornare viverra arcu ac aliquam. In ut dignissim ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eros enim, euismod sit amet dictum at, venenatis vel mauris. Proin pharetra velit a dui egestas accumsan.

57 | 58 |

Duis id quam at lacus porttitor iaculis vel eget eros. Morbi vulputate elit ac lacus porta sed tempus sem facilisis. Praesent semper neque vel nulla molestie nec faucibus nisi vulputate. Quisque semper est ultrices tortor volutpat adipiscing. Aenean lacus turpis, fringilla a commodo eget, congue et urna. Phasellus tortor urna, tempus id dapibus eget, porttitor at eros. Curabitur rutrum rutrum massa, eget mollis massa luctus facilisis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer ultricies diam non urna sodales a hendrerit purus semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras non ipsum arcu, a blandit mauris. Cras scelerisque adipiscing nisi, in pulvinar nibh pellentesque vitae. In hac habitasse platea dictumst. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ultricies odio euismod orci malesuada nec tincidunt orci fermentum.

59 | 60 |

Donec ullamcorper dui et ipsum ultrices sed volutpat felis semper. Sed volutpat tincidunt fringilla. Curabitur at erat vel risus posuere aliquet. Proin nec sapien et nisi hendrerit dictum ut at leo. Nullam vitae mauris quam. Fusce a porta dui. Sed tempor, urna sit amet tempus rhoncus, nibh libero hendrerit neque, eget vestibulum nibh purus sit amet odio. Duis orci sapien, ullamcorper vitae sollicitudin et, bibendum vel tortor. Cras pulvinar suscipit ipsum quis volutpat. Integer purus mi, sollicitudin at molestie vitae, congue sit amet lectus. Cras accumsan, metus sed pulvinar vehicula, dui odio dapibus velit, a porta purus turpis in nulla. Maecenas ligula neque, volutpat ut sodales sit amet, volutpat a nisl. Aliquam erat volutpat.

61 | 62 | 63 | -------------------------------------------------------------------------------- /samples/phpab-tags/index.php: -------------------------------------------------------------------------------- 1 | add_variation('logos', '99% of people are concerned about fluffy bunnies.'); 6 | $my_test->add_variation('ethos', 'Fluffy bunnies deserve to be protected.'); 7 | $my_test->add_variation('pathos', 'Fluffy bunnies are cute and snuggly.'); 8 | 9 | ?> 10 | 11 | 12 | Saving fluffy bunnies, one A/B test at a time 13 | 14 | 32 | 33 | 43 | 44 | 45 | 46 | 47 |

{phpab headline}OMG! Fluffy bunnies!{/phpab headline}

48 | 49 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sapien orci, tincidunt nec euismod sit amet, porttitor sed massa. Vestibulum sollicitudin risus eu quam consequat elementum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vitae nunc eget tellus semper rhoncus imperdiet ac arcu. Suspendisse et felis lacus. Suspendisse neque dui, suscipit et vehicula at, lacinia at leo. Pellentesque at dignissim leo. Phasellus felis lectus, varius sit amet sodales vel, convallis sit amet leo. Curabitur mi sapien, tristique ac rutrum at, posuere non dolor. Sed non fermentum nisi. Vivamus sapien nisi, blandit at imperdiet vitae, tempus eget leo. Phasellus ultrices interdum pretium. Nunc ornare viverra arcu ac aliquam. In ut dignissim ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eros enim, euismod sit amet dictum at, venenatis vel mauris. Proin pharetra velit a dui egestas accumsan.

50 | 51 |

Duis id quam at lacus porttitor iaculis vel eget eros. Morbi vulputate elit ac lacus porta sed tempus sem facilisis. Praesent semper neque vel nulla molestie nec faucibus nisi vulputate. Quisque semper est ultrices tortor volutpat adipiscing. Aenean lacus turpis, fringilla a commodo eget, congue et urna. Phasellus tortor urna, tempus id dapibus eget, porttitor at eros. Curabitur rutrum rutrum massa, eget mollis massa luctus facilisis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer ultricies diam non urna sodales a hendrerit purus semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras non ipsum arcu, a blandit mauris. Cras scelerisque adipiscing nisi, in pulvinar nibh pellentesque vitae. In hac habitasse platea dictumst. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ultricies odio euismod orci malesuada nec tincidunt orci fermentum.

52 | 53 |

Donec ullamcorper dui et ipsum ultrices sed volutpat felis semper. Sed volutpat tincidunt fringilla. Curabitur at erat vel risus posuere aliquet. Proin nec sapien et nisi hendrerit dictum ut at leo. Nullam vitae mauris quam. Fusce a porta dui. Sed tempor, urna sit amet tempus rhoncus, nibh libero hendrerit neque, eget vestibulum nibh purus sit amet odio. Duis orci sapien, ullamcorper vitae sollicitudin et, bibendum vel tortor. Cras pulvinar suscipit ipsum quis volutpat. Integer purus mi, sollicitudin at molestie vitae, congue sit amet lectus. Cras accumsan, metus sed pulvinar vehicula, dui odio dapibus velit, a porta purus turpis in nulla. Maecenas ligula neque, volutpat ut sodales sit amet, volutpat a nisl. Aliquam erat volutpat.

54 | 55 | 56 | --------------------------------------------------------------------------------