#!/usr/bin/env php
find('a') as $a) {
check_a($a, $reporter);
}
}
/**
* Scan an tag
*
* @param object $a
* @param callable $reporter
* @return void
*/
function check_a($a, $reporter) {
if (!$a->hasAttribute('href')) {
// anchor, don't care
return;
}
$href = trim($a->getAttribute('href'));
if (preg_match('/javascript:/', $href)) {
$reporter('javascript-url', " has javascript url: $href");
return;
}
if ($href == '#' && $a->hasAttribute('onclick')) {
$onclick = $a->getAttribute('onclick');
if (!js_returns_false($onclick) && !js_returns_func($onclick)) {
$reporter('a-no-return', " has href=# but handler fails to return false: $onclick");
return;
}
}
if ($href != '#' && $a->hasAttribute('onclick')) {
$onclick = $a->getAttribute('onclick');
$reporter('a-double-action', " has both URL ($href) and onclick ($onclick)");
return;
}
}
/**
* Determine if snippet of JS returns strictly false
* @param $js
* @return bool
*/
function js_returns_false($js) {
return
// last in a series of statements
preg_match('/; *return +false *; *$/', $js)
||
// only statement
preg_match('/^ *return +false *;? *$/', $js);
}
/**
* Determine if snippet of JS returns a function call
* @param $js
* @return int
*/
function js_returns_func($js) {
return preg_match('/^ *return +[a-zA-Z0-9\._$]+\(/', $js);
}