Seguir redirecciones en PHP con SAFE_MODE activado y/o open_basedir desactivado.

Si tenemos un servidor PHP con SAFE_MODE activo o open_basedir desactivado el módulo cURL de PHP no nos dejará seguir las redirecciones de las URLs, dándonos un bonito error:

Warning: curl_setopt() [function.curl-setopt]: CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir is set in XXXXXX

Para seguir las redirecciones hemos de hacerlo manualmente. Para ello encontré una web donde explicaban cómo abrir las URLs siguiendo las redirecciones, y he modificado el código para que retorne la URL final en lugar de los datos, de los cuales ni tan siquiera cargue el body, para evitar carga al servidor:
function get_web_page_url($url){
$go = curl_init($url);
curl_setopt ($go, CURLOPT_URL, $url);
//follow on location problems
$last = curl_redir_exec($go);
curl_close($go);
return $last;
}

//follow on location problems workaround
function curl_redir_exec($ch) {
static $curl_loops = 0;
static $curl_max_loops = 10;
if ($curl_loops++ >= $curl_max_loops) {
$curl_loops = 0;
return FALSE;
}
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
$data = curl_exec($ch);
list($header, $data) = explode("\n\n", $data, 2);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code >= 301 && $http_code <= 305) {
$matches = array();
preg_match('/Location:(.*?)\n/', $header, $matches);
$url = @parse_url(trim(array_pop($matches)));
if (!$url) {
//couldn't process the url to redirect to
$curl_loops = 0;
return false;
}
$last_url = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
if (!$url['scheme']) $url['scheme'] = $last_url['scheme'];
if (!$url['host']) $url['host'] = $last_url['host'];
if (!$url['path']) $url['path'] = $last_url['path'];
$new_url = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query']?'?'.$url['query']:'');
curl_setopt($ch, CURLOPT_URL, $new_url);
return curl_redir_exec($ch);
}
else {
$curl_loops=0;
$last_url = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
return $last_url['scheme'] . '://' . $last_url['host'] . $last_url['path'] . ($last_url['query']?'?'.$last_url['query']:'');
}
}

Reordad que para usar URLs complejas (ej: espacios, %, etc) debemos codificarlas correctamente. Para ello tembién tengo una función:

function normalize_url( $url ) {
// Normalizamos el nombre para el fopen; se ha de hacer un rawurlencode para codificar todos los caracteres menos el separador (/) y el http(s):
$urlarr = explode("/",$url);
foreach( $urlarr as $key => $valor )
if( $valor != "http:" && "https:" ) $urli[$key] = rawurlencode($valor);
else $urli[$key] = $valor;
return implode("/",$urli);
}


Un ejemplo de uso, para obtener la información de una imágen, tal y como se hace en las imágenes aleatórias, sería:

  $urli = get_web_page_url( normalize_url($url) );
$datos = getimagesize($urli);