0

在迁移到更好的托管订阅时,需要很长时间才能找出导致我的网站出现故障的原因。

我使用“自制”的 uniqueId 生成器来生成必须唯一但这种唯一性不是随机的所有内容。我使用它在多个服务之间进行通信,为文件、文章等生成可重现的唯一“数字”。

这是我制作的并且从未遇到过问题(我认为它以前从未在 64 位系统上运行过?)来生成唯一 ID 的功能。我知道这种独特性是有限的(64.000),但直到现在才导致问题。

function suGetHashCode($s)
{
 $hash=0;
 $c=(is_string($s))?strlen($s):0;
 $i=0;
 while($i<$c) 
 {
   $hash = (($hash << 5)-$hash)+ord($s{$i++});
   //hash = hash & hash; // Convert to 32bit integer
 }
 return ( $hash < 0 )?(($hash*-1)+0xFFFFFFFF):$hash; // convert to unsigned int
} 

function suUniqueId( $s, $bAddLen = false )
{ 
  $i = base_convert( suGetHashCode( $s ), 10, 32 );
  if( $bAddLen && is_string($s) )
   { $i.=('-'.suGetLz( dechex( strlen($s)*4 ), 3 )); } 

  return $i; 
}

function suGetLz( $i, $iMaxLen ) // Leading zero
{
  if( !is_numeric( $i ) || $i < 0 || $iMaxLen <= 0 )
   { return $i; }
  $c = strlen( $i );
  while( $c < $iMaxLen )
   { $c++; $i='0'.$i; } 
  return $i;
}   

整数的最大 int 值在新系统上:

PHP_INT_MAX = 9223372036854775807

在其他系统上是:

PHP_INT_MAX = 2147483647

好吧,我不是数学家,我认为这是因为负数时 0xFFFFFFFF 增量导致了问题(我认为在这个新系统上它永远不会是负数)。

但是我怎样才能改变它在其他系统上产生相同的唯一 ID 的功能呢?

例如:它为新托管服务器上的不同字符串生成相同的 id:

 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: 1l5kc37uicb  
 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce the same id as above: 1l5kc37uicb

但是,这必须像在旧系统上一样:

 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: a46q6nd  
 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: 2mirj1h

注意:字符串被分成几部分以避免堆栈溢出,请参阅此链接。

编辑删除文件名

有谁如何处理这个问题?

4

2 回答 2

1

我建议您在处理完每个字符后截断:

$hash = (($hash << 5)-$hash)+ord($s{$i++});
$hash = $hash & 0xFFFFFFFF; // Convert to 32bit integer

至少在我的 64 位系统上,这导致了2mirj1h您第二个示例中的期望,尽管没有这个修改我得到了1c6ta2qjga7,而不是1l5kc37uicb像你那样。

我还将返回值更改为简单地 return $hash。要么它可以正确表示无符号的 32 位数字,那么前面的掩码应该强制解释。或者您的系统无法表示这些,那么添加的计算也不会让您到达那里,您必须将数字拆分为位组并单独将它们字符串化。

当然,最简单的解决方案是使用一些完善的通用散列算法,例如使用hash函数。如果你做到这一点,添加一些秘密盐可能会让你受到攻击。如果这样的哈希码的结果太长,你可以简单地取一部分输出。您可以以任何您喜欢的方式转换基数,因此您不必使用散列常用的十六进制表示法。使用加密哈希也可以减少发生冲突的机会;例如,在您的情况下generbM.js,同一路径中的文档将产生相同的哈希。

于 2013-10-06T23:08:41.653 回答
1

如果我是你,我会编写一个单元测试来确保你在 32 位和 64 位机器上获得相同的结果。

应该像这样更改循环:

while($i<$c) 
{
  $hash = (($hash << 5)-$hash)+ord($s{$i++});
  hash = hash & 0xFFFFFFFF; // Convert to 32bit integer
}
$hash = ( $hash < 0 )?(($hash*-1)+0xFFFFFFFF):$hash; // convert to unsigned int
return $hash & 0xFFFFFFFF; // Convert to 32bit integer

您的单元测试可以在 32 位版本上针对原始版本运行并保存输出。然后在 64 位上运行它并与那些 32 位结果进行比较。如果任何一个不同,您就知道您仍然没有 1 对 1 的等价物。

于 2013-10-06T23:41:38.747 回答