123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- <?php
- namespace App\Base\Library;
- use App\Base\Exceptions\ApiException;
- use App\Basic\Facades\UrlsFacade;
- class Shorty {
- /**
- * Default characters to use for shortening.
- *
- * @var string
- */
- private $chars = 'DUuW9Pd0lGE2xZp4aIwOrHYkMA7fCi1Te53VzL6gRKnmtSsFoyNQvcjBqJhX8b';
- /**
- * Salt for id encoding.
- *
- * @var string
- */
- private $salt = '';
- /**
- * Length of number padding.
- */
- private $padding = 1;
- /**
- * Hostname
- */
- private $hostname = '';
- /**
- * PDO database connection.
- *
- * @var object
- */
- private $connection = null;
- /**
- * Whitelist of IPs allowed to save URLs.
- * If the list is empty, then any IP is allowed.
- *
- * @var array
- */
- private $whitelist = array();
- /**
- * Constructor
- *
- * @param string $hostname Hostname
- * @param object $connection Database connection
- */
- public function __construct($hostname) {
- $this->hostname = $hostname;
- }
- /**
- * Gets the character set for encoding.
- *
- * @return string Set of characters
- */
- public function get_chars() {
- return $this->chars;
- }
- /**
- * Sets the character set for encoding.
- *
- * @param string $chars Set of characters
- */
- public function set_chars($chars) {
- if (!is_string($chars) || empty($chars)) {
- throw new Exception('Invalid input.');
- }
- $this->chars = $chars;
- }
- /**
- * Gets the salt string for encoding.
- *
- * @return string Salt
- */
- public function get_salt() {
- return $this->salt;
- }
- /**
- * Sets the salt string for encoding.
- *
- * @param string $salt Salt string
- */
- public function set_salt($salt) {
- $this->salt = $salt;
- }
- /**
- * Gets the padding length.
- *
- * @return int Padding length
- */
- public function get_padding() {
- return $this->padding;
- }
- /**
- * Sets the padding length.
- *
- * @param int $padding Padding length
- */
- public function set_padding($padding) {
- $this->padding = $padding;
- }
- /**
- * Converts an id to an encoded string.
- *
- * @param int $n Number to encode
- * @return string Encoded string
- */
- public function encode($n) {
- $k = 0;
- if ($this->padding > 0 && !empty($this->salt)) {
- $k = self::get_seed($n, $this->salt, $this->padding);
- $n = (int)($k.$n);
- }
- return self::num_to_alpha($n, $this->chars);
- }
- /**
- * Converts an encoded string into a number.
- *
- * @param string $s String to decode
- * @return int Decoded number
- */
- public function decode($s) {
- $n = self::alpha_to_num($s, $this->chars);
- return (!empty($this->salt)) ? substr($n, $this->padding) : $n;
- }
- /**
- * Gets a number for padding based on a salt.
- *
- * @param int $n Number to pad
- * @param string $salt Salt string
- * @param int $padding Padding length
- * @return int Number for padding
- */
- public static function get_seed($n, $salt, $padding) {
- $hash = md5($n.$salt);
- $dec = hexdec(substr($hash, 0, $padding));
- $num = $dec % pow(10, $padding);
- if ($num == 0) $num = 1;
- $num = str_pad($num, $padding, '0');
- return $num;
- }
- /**
- * Converts a number to an alpha-numeric string.
- *
- * @param int $num Number to convert
- * @param string $s String of characters for conversion
- * @return string Alpha-numeric string
- */
- public static function num_to_alpha($n, $s) {
- $b = strlen($s);
- $m = $n % $b;
- if ($n - $m == 0) return substr($s, $n, 1);
- $a = '';
- while ($m > 0 || $n > 0) {
- $a = substr($s, $m, 1).$a;
- $n = ($n - $m) / $b;
- $m = $n % $b;
- }
- return $a;
- }
- /**
- * Converts an alpha numeric string to a number.
- *
- * @param string $a Alpha-numeric string to convert
- * @param string $s String of characters for conversion
- * @return int Converted number
- */
- public static function alpha_to_num($a, $s) {
- $b = strlen($s);
- $l = strlen($a);
- for ($n = 0, $i = 0; $i < $l; $i++) {
- $n += strpos($s, substr($a, $i, 1)) * pow($b, $l - $i - 1);
- }
- return $n;
- }
- /**
- * Looks up a URL in the database by id.
- *
- * @param string $id URL id
- * @return array URL record
- */
- public function fetch($id) {
- $statement = $this->connection->prepare(
- 'SELECT * FROM urls WHERE id = ?'
- );
- $statement->execute(array($id));
- return $statement->fetch(PDO::FETCH_ASSOC);
- }
- /**
- * Attempts to locate a URL in the database.
- *
- * @param string $url URL
- * @return array URL record
- */
- public function find($url) {
- return UrlsFacade::findOneBy([
- "url" => $url,
- "status" => 0
- ], "id,url");
- }
- /**
- * Stores a URL in the database.
- *
- * @param string $url URL to store
- * @return int Insert id
- */
- public function store($url) {
- $info = UrlsFacade::save([
- "url" => $url,
- "status" => 0
- ]);
- return $info->id;
- }
- /**
- * Updates statistics for a URL.
- *
- * @param int $id URL id
- */
- public function update($id, $key) {
- return UrlsFacade::update($id, [
- "key" => $key
- ]);
- }
- /**
- * Sends a redirect to a URL.
- *
- * @param string $url URL
- */
- public function redirect($url) {
- header("Location: $url", true, 301);
- exit();
- }
- /**
- * Sends a 404 response.
- */
- public function not_found() {
- header('Status: 404 Not Found');
- exit(
- '<h1>404 Not Found</h1>'.
- str_repeat(' ', 512)
- );
- }
- /**
- * Sends an error message.
- *
- * @param string $message Error message
- */
- public function error($message) {
- exit("<h1>$message</h1>");
- }
- /**
- * Adds an IP to allow saving URLs.
- *
- * @param string|array $ip IP address or array of IP addresses
- */
- public function allow($ip) {
- if (is_array($ip)) {
- $this->whitelist = array_merge($this->whitelist, $ip);
- }
- else {
- array_push($this->whitelist, $ip);
- }
- }
- /**
- * Starts the program.
- */
- public function run($url)
- {
- $url = urldecode($url);
- // If adding a new URL
- if (!empty($url)) {
- if (!empty($this->whitelist) && !in_array($_SERVER['REMOTE_ADDR'], $this->whitelist)) {
- throw new ApiException("", "请求地址不在白名单中");
- }
- if (preg_match('/^http[s]?\:\/\/[\w]+/', $url)) {
- $result = $this->find($url); //查询是否存在
- // Not found, so save it
- if (empty($result)) {
- $id = $this->store($url); // 新增
- $code = $this->encode($id);
- $url = $this->hostname . '/' . $code;
- $this->update($id, $code);
- } else {
- $url = $this->hostname . '/' . $this->encode($result['id']);
- }
- return $url;
- } else {
- throw new ApiException("", "地址错误");
- }
- } else {
- throw new ApiException("", "地址错误");
- }
- }
- }
|