短縮URLの展開処理(PHP)の改版
古き良き時代は終わりを告げ、Webサイトへの接続はHTTP over SSL/TLSが当たり前になった。
そんなわけで、久々に引っ張り出した短縮URL展開プログラムのPHP版が、このままじゃダメだ!という状況になっていたので改版。
<?php class ShortURL { private static $services = null; private static function initServices() { if (!is_null(self::$services)) { return; } self::$services = array(); self::$services[] = parse_url("http://t.co/"); self::$services[] = parse_url("https://t.co/"); self::$services[] = parse_url("http://bit.ly/"); self::$services[] = parse_url("https://bit.ly/"); self::$services[] = parse_url("http://goo.gl/"); self::$services[] = parse_url("https://goo.gl/"); self::$services[] = parse_url("http://tinyurl.com/"); self::$services[] = parse_url("https://tinyurl.com/"); self::$services[] = parse_url("http://tiny.one/"); self::$services[] = parse_url("https://tiny.one/"); self::$services[] = parse_url("http://htn.to/"); self::$services[] = parse_url("https://htn.to/"); } public static function expand($shortUrl) { self::initServices(); $parsed = parse_url($shortUrl); if (!$parsed) { return $shortUrl; } $isTarget = false; foreach (self::$services as $service) { if ($service['scheme'] === $parsed['scheme'] && $service['host'] === $parsed['host']) { $isTarget = true; break; } } if (!$isTarget) { return $shortUrl; } $defaultPort = $service['scheme'] === 'https' ? 443 : 80; $host = ($service['scheme'] === 'https' ? 'ssl://' : '') . $parsed['host']; $port = isset($parsed['port']) && $parsed['port'] > 0 ? $parsed['port'] : $defaultPort; $sock = fsockopen($host, $port); if (!$sock) { throw new Exception($shortUrl . ': Connection failed.'); } fwrite($sock, "GET {$parsed['path']} HTTP/1.1\r\n"); fwrite($sock, "Host: {$parsed['host']}\r\n"); fwrite($sock, "Accept: */*\r\n"); fwrite($sock, "\r\n"); $returnUrl = null; while (($header = fgets($sock))) { $header = trim($header); if (!$header) { break; } if (preg_match("/^Location: /i", $header)) { $returnUrl = preg_replace("/^Location: /i", "", $header); } } fclose($sock); if ($returnUrl) { return $returnUrl; } else { return $shortUrl; } } }
ポイントは、HTTP over SSL/TLSの際には「デフォルトポート番号が443」ということと、「fsockopenを使うときは "ssl://" を前に付ける必要がある」ということかしら。 後は、Locationヘッダを全て小文字で返してくる輩もいたので、その対応も追加(preg_match/preg_replaceの "i" オプションの指定)。
呼び出しのサンプル。 これに適当にHTMLコードを付け加えてあげると、最大100段まで展開・表示してくれます。
<pre><?php if (isset($_POST['url'])) { include('./ShortURL.php'); $url = $_POST['url']; try { printf("%s\r\n", htmlspecialchars($url)); $indent = "┗"; for ($i = 0; $i < 100 && ($newUrl = ShortURL::expand($url)) !== $url; ++$i) { printf("%s\r\n", $indent . htmlspecialchars($newUrl)); $indent = " " . $indent; $url = $newUrl; } } catch (Exception $e) { echo htmlspecialchars($e->getMessage()); } } ?></pre>
実際に動くものをサンプルコードと合わせて提示するサイトを作りたいと思いながら幾星霜・・・