php中通过Hashids将整数转化为唯一字符串

本文介绍了一种名为Hashids的技术,该技术能够将数字ID转换为无规律的字符串形式,有效防止数据被爬虫抓取。文章详细展示了如何通过配置项调整加密后的字符串长度、盐值及字符集,并提供了具体的PHP实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个类主要是前台显示的主键ID转化成一串无规律的字符串,比较像 Youtube、Youku、Weibo之类的 id 名,从某种意义上可以防采集。

首先设置配置信息(可以不设置配置信息通过传参形式)

return [
    // Hashids 的配置项
    'length'   => 9, // 加密字符串长度
    'salt'     => 'tphui', // 加密钥匙
    'alphabet' => '', // 字符仓库,不填写默认为扩展里的字符仓库
];

导入Hashids函数

<?php

/*
	
	Hashids
	http://hashids.org/php
	(c) 2013 Ivan Akimov
	
	https://github.com/ivanakimov/hashids.php
	hashids may be freely distributed under the MIT license.
	
*/

namespace Hashids;

/**
 * HashGenerator is a contract for generating hashes
 */
interface HashGenerator {
	
	/**
	 * Encodes a variable number of parameters to generate a hash
	 * 
	 * @param mixed ...
	 * 
	 * @return string the generated hash
	 */
	public function encode();
	
	/**
	 * Decodes a hash to the original parameter values
	 * 
	 * @param string $hash the hash to decode
	 * 
	 * @return array
	 */
	public function decode($hash);
	
	/**
	 * Encodes hexadecimal values to generate a hash
	 * 
	 * @param string $str hexadecimal string
	 * 
	 * @return string the generated hash
	 */
	public function encode_hex($str);
	
	/**
	 * Decodes hexadecimal hash
	 * 
	 * @param string $hash
	 * 
	 * @return string hexadecimal string
	 */
	public function decode_hex($hash);
	
}
<?php

/*
	
	Hashids
	http://hashids.org/php
	(c) 2013 Ivan Akimov
	
	https://github.com/ivanakimov/hashids.php
	hashids may be freely distributed under the MIT license.
	
*/

namespace Hashids;

class Hashids implements HashGenerator {
	
	const VERSION = '1.0.5';
	
	/* internal settings */
	
	const MIN_ALPHABET_LENGTH = 16;
	const SEP_DIV = 3.5;
	const GUARD_DIV = 12;
	
	/* error messages */
	
	const E_ALPHABET_LENGTH = 'alphabet must contain at least %d unique characters';
	const E_ALPHABET_SPACE = 'alphabet cannot contain spaces';
	
	/* set at constructor */
	
	private $_alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
	private $_seps = 'cfhistuCFHISTU';
	private $_min_hash_length = 0;
	private $_math_functions = array();
	private $_max_int_value = 1000000000;

    private static $instance; //单例

    /**
     * 初始化
     * @param array $options
     * @return static
     */
    public static function instance($length = null, $salt = null, $alphabet = null)
    {
        if (is_null(self::$instance)) {
            if ($length === null) $length = config("hashids.length");
            if ($salt === null) $salt = config("hashids.salt");
            if ($alphabet === null) $alphabet = config("hashids.alphabet");

            self::$instance = new static($salt, $length, $alphabet);
        }
        return self::$instance;
    }
	
	public function __construct($salt = '', $min_hash_length = 8, $alphabet = '') {
		
		/* if either math precision library is present, raise $this->_max_int_value */
		
		if (function_exists('gmp_add')) {
			$this->_math_functions['add'] = 'gmp_add';
			$this->_math_functions['div'] = 'gmp_div';
			$this->_math_functions['str'] = 'gmp_strval';
		} else if (function_exists('bcadd')) {
			$this->_math_functions['add'] = 'bcadd';
			$this->_math_functions['div'] = 'bcdiv';
			$this->_math_functions['str'] = 'strval';
		}
		
		$this->_lower_max_int_value = $this->_max_int_value;
		if ($this->_math_functions) {
			$this->_max_int_value = PHP_INT_MAX;
		}
		
		/* handle parameters */
		
		$this->_salt = $salt;
		
		if ((int)$min_hash_length > 0) {
			$this->_min_hash_length = (int)$min_hash_length;
		}
		
		if ($alphabet) {
			$this->_alphabet = implode('', array_unique(str_split($alphabet)));
		}
		
		if (strlen($this->_alphabet) < self::MIN_ALPHABET_LENGTH) {
			throw new \Exception(sprintf(self::E_ALPHABET_LENGTH, self::MIN_ALPHABET_LENGTH));
		}
		
		if (is_int(strpos($this->_alphabet, ' '))) {
			throw new \Exception(self::E_ALPHABET_SPACE);
		}
		
		$alphabet_array = str_split($this->_alphabet);
		$seps_array = str_split($this->_seps);
		
		$this->_seps = implode('', array_intersect($alphabet_array, $seps_array));
		$this->_alphabet = implode('', array_diff($alphabet_array, $seps_array));
		$this->_seps = $this->_consistent_shuffle($this->_seps, $this->_salt);
		
		if (!$this->_seps || (strlen($this->_alphabet) / strlen($this->_seps)) > self::SEP_DIV) {
			
			$seps_length = (int)ceil(strlen($this->_alphabet) / self::SEP_DIV);
			
			if ($seps_length == 1) {
				$seps_length++;
			}
			
			if ($seps_length > strlen($this->_seps)) {
				
				$diff = $seps_length - strlen($this->_seps);
				$this->_seps .= substr($this->_alphabet, 0, $diff);
				$this->_alphabet = substr($this->_alphabet, $diff);
				
			} else {
				$this->_seps = substr($this->_seps, 0, $seps_length);
			}
			
		}
		
		$this->_alphabet = $this->_consistent_shuffle($this->_alphabet, $this->_salt);
		$guard_count = (int)ceil(strlen($this->_alphabet) / self::GUARD_DIV);
		
		if (strlen($this->_alphabet) < 3) {
			$this->_guards = substr($this->_seps, 0, $guard_count);
			$this->_seps = substr($this->_seps, $guard_count);
		} else {
			$this->_guards = substr($this->_alphabet, 0, $guard_count);
			$this->_alphabet = substr($this->_alphabet, $guard_count);
		}
		
	}
	
	public function encode() {
		
		$ret = '';
		$numbers = func_get_args();
		
		if (func_num_args() == 1 && is_array(func_get_arg(0))) {
			$numbers = $numbers[0];
		}
		
		if (!$numbers) {
			return $ret;
		}
		
		foreach ($numbers as $number) {
			
			$is_number = ctype_digit((string)$number);
			
			if (!$is_number || $number < 0 || $number > $this->_max_int_value) {
				return $ret;
			}
			
		}
		
		return $this->_encode($numbers);
		
	}
	
	public function decode($hash) {
		
		$ret = array();
		
		if (!$hash || !is_string($hash) || !trim($hash)) {
			return $ret;
		}
		
		return $this->_decode(trim($hash), $this->_alphabet);
		
	}
	
	public function encode_hex($str) {
		
		if (!ctype_xdigit((string)$str)) {
			return '';
		}
		
		$numbers = trim(chunk_split($str, 12, ' '));
		$numbers = explode(' ', $numbers);
		
		foreach ($numbers as $i => $number) {
			$numbers[$i] = hexdec('1' . $number);
		}
		
		return call_user_func_array(array($this, 'encode'), $numbers);
		
	}
	
	public function decode_hex($hash) {
		
		$ret = "";
		$numbers = $this->decode($hash);
		
		foreach ($numbers as $i => $number) {
			$ret .= substr(dechex($number), 1);
		}
		
		return $ret;
		
	}
	
	public function get_max_int_value() {
		return $this->_max_int_value;
	}
	
	private function _encode(array $numbers) {
		
		$alphabet = $this->_alphabet;
		$numbers_size = sizeof($numbers);
		$numbers_hash_int = 0;
		
		foreach ($numbers as $i => $number) {
			$numbers_hash_int += ($number % ($i + 100));
		}
		
		$lottery = $ret = $alphabet[$numbers_hash_int % strlen($alphabet)];
		foreach ($numbers as $i => $number) {
			
			$alphabet = $this->_consistent_shuffle($alphabet, substr($lottery . $this->_salt . $alphabet, 0, strlen($alphabet)));
			$ret .= $last = $this->_hash($number, $alphabet);
			
			if ($i + 1 < $numbers_size) {
				$number %= (ord($last) + $i);
				$seps_index = $number % strlen($this->_seps);
				$ret .= $this->_seps[$seps_index];
			}
			
		}
		
		if (strlen($ret) < $this->_min_hash_length) {
			
			$guard_index = ($numbers_hash_int + ord($ret[0])) % strlen($this->_guards);
			
			$guard = $this->_guards[$guard_index];
			$ret = $guard . $ret;
			
			if (strlen($ret) < $this->_min_hash_length) {
				
				$guard_index = ($numbers_hash_int + ord($ret[2])) % strlen($this->_guards);
				$guard = $this->_guards[$guard_index];
				
				$ret .= $guard;
				
			}
			
		}
		
		$half_length = (int)(strlen($alphabet) / 2);
		while (strlen($ret) < $this->_min_hash_length) {
			
			$alphabet = $this->_consistent_shuffle($alphabet, $alphabet);
			$ret = substr($alphabet, $half_length) . $ret . substr($alphabet, 0, $half_length);
			
			$excess = strlen($ret) - $this->_min_hash_length;
			if ($excess > 0) {
				$ret = substr($ret, $excess / 2, $this->_min_hash_length);
			}
			
		}
		
		return $ret;
		
	}
	
	private function _decode($hash, $alphabet) {
		
		$ret = array();
		
		$hash_breakdown = str_replace(str_split($this->_guards), ' ', $hash);
		$hash_array = explode(' ', $hash_breakdown);
		
		$i = 0;
		if (sizeof($hash_array) == 3 || sizeof($hash_array) == 2) {
			$i = 1;
		}
		
		$hash_breakdown = $hash_array[$i];
		if (isset($hash_breakdown[0])) {
			
			$lottery = $hash_breakdown[0];
			$hash_breakdown = substr($hash_breakdown, 1);
			
			$hash_breakdown = str_replace(str_split($this->_seps), ' ', $hash_breakdown);
			$hash_array = explode(' ', $hash_breakdown);
			
			foreach ($hash_array as $sub_hash) {
				$alphabet = $this->_consistent_shuffle($alphabet, substr($lottery . $this->_salt . $alphabet, 0, strlen($alphabet)));
				$ret[] = (int)$this->_unhash($sub_hash, $alphabet);
			}
			
			if ($this->_encode($ret) != $hash) {
				$ret = array();
			}
			
		}
		
//		return $ret;
        //修改为直接返回字符串
		return $ret[0];

	}
	
	private function _consistent_shuffle($alphabet, $salt) {
		
		if (!strlen($salt)) {
			return $alphabet;
		}
		
		for ($i = strlen($alphabet) - 1, $v = 0, $p = 0; $i > 0; $i--, $v++) {
			
			$v %= strlen($salt);
			$p += $int = ord($salt[$v]);
			$j = ($int + $v + $p) % $i;
			
			$temp = $alphabet[$j];
			$alphabet[$j] = $alphabet[$i];
			$alphabet[$i] = $temp;
			
		}
		
		return $alphabet;
		
	}
	
	private function _hash($input, $alphabet) {
		
		$hash = '';
		$alphabet_length = strlen($alphabet);
		
		do {
			
			$hash = $alphabet[$input % $alphabet_length] . $hash;
			if ($input > $this->_lower_max_int_value && $this->_math_functions) {
				$input = $this->_math_functions['str']($this->_math_functions['div']($input, $alphabet_length));
			} else {
				$input = (int)($input / $alphabet_length);
			}
			
		} while ($input);
		
		return $hash;
		
	}
	
	private function _unhash($input, $alphabet) {
		
		$number = 0;
		if (strlen($input) && $alphabet) {
			
			$alphabet_length = strlen($alphabet);
			$input_chars = str_split($input);
			
			foreach ($input_chars as $i => $char) {
				
				$pos = strpos($alphabet, $char);
				if ($this->_math_functions) {
					$number = $this->_math_functions['str']($this->_math_functions['add']($number, $pos * pow($alphabet_length, (strlen($input) - $i - 1))));
				} else {
					$number += $pos * pow($alphabet_length, (strlen($input) - $i - 1));
				}
				
			}
			
		}
		
		return $number;
		
	}
	
}
调用Hashids类进行加密解密ID
        $id = 11;
        $hashids = \Hashids\Hashids::instance(6, 'tphui');
        $encode_id = $hashids->encode($id); //加密
        var_dump($encode_id);
        $decode_id = $hashids->decode($encode_id); //解密
        var_dump($decode_id);
        exit;

E:\wamp64\www\tphui\application\index\controller\Index.php:11:string 'r8qvlw' (length=6)
E:\wamp64\www\tphui\application\index\controller\Index.php:13:int 11


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值