在PHP中将资源用作数组索引

6

我目前使用以下哈希方法来查找资源

$foo = socket_create(...);
$bar = socket_create(...);

$map[(int)$foo] = 'foo';
$map[(int)$bar] = 'bar';

echo $map[(int)$foo]; // "foo"

这种情况下,整数强制转换是否是最佳选择?如果不是,有哪些更好或更有效的哈希方法?这些查找将在一个集合中执行,每秒多次在紧密的循环(套接字轮询)中进行,因此我已经排除了迭代式的解决方案。
编辑: 为了更好地解释我的情况,socket_select() 函数通过引用获取套接字资源数组,并修改它们,以便在函数调用后,它们仅包含已更改的资源(例如准备从中读取)。我使用 Socket 类作为套接字资源的包装器,使我的代码更抽象化和可测试。
$socketObject = new Socket($socketResource);

我的另一个类会保留所有需要在调用 socket_select() 时轮询的套接字资源列表:

$reads = [$socketResource1, $socketResource2, ...];
socket_select($reads, null, null, 0);

在调用 socket_select() 后,我知道哪些套接字 资源 发生了变化,但是为了在我的代码中执行任何有意义的操作,我需要知道这些资源对应的套接字 对象。因此,我需要一种将套接字资源映射到它们的对象的方法:
foreach ($reads as $socketResource) {
    // Which socket object does $socketResource correspond to here?
    // Currently, I use a solution like this:
    $socketObject = $this->map[(int)$socketResource];
    // Unfortunately, this behavior isn't guaranteed, so it isn't reliable...
}
4个回答

7
当将投入资源到整数时,观察到的行为是未定义的(请参见页面底部的注意事项)。因此,即使这现在可行且长期可靠, 您也必须意识到它不是您可以依赖而不需通知就能更改的东西。 澄清后编辑: 不要把资源作为键,使用两个数组。一个将Socket对象的哈希映射到实际对象。另一个将相同的哈希映射到资源。然后将后一个数组传递给socket_select。在函数不更改数组键的前提下,您可以遍历数组并使用键在O(1)中查找Socket:
$r1 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$r2 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

$s1 = new Socket($foo);
$s2 = new Socket($bar);

$socketMap = array(
    spl_object_hash($s1) => $s1,
    spl_object_hash($s2) => $s2
);

$reads = array(
    spl_object_hash($s1) => $r1,
    spl_object_hash($s2) => $r2
);

socket_select($reads, null, null, 0);

foreach (array_keys($reads) as $hash) {
    $socketObject = $socketMap[$hash];
}

更新:将资源转换为整数不再是未定义的,如链接的手册页面所示。如果将资源转换为整数,则结果将是PHP在运行时分配给该资源的唯一资源编号。


我从来没有想过将关联数组传递给 socket_select() 函数;这可能会起作用。我稍后会测试一下。我会跟进并让你知道结果。谢谢! - FtDRbwLXw6
2
我刚刚测试了一下,键通过 socket_select() 调用得以保留,一切都很顺利。我已经接受了你的答案并开始了悬赏(在24小时后才能颁发),因为我非常感谢你的努力和帮助。谢谢! - FtDRbwLXw6
1
注意:将资源转换为整数不再是未定义的,如链接手册页面所示。如果将资源转换为整数,则结果将是PHP在运行时分配给该资源的唯一资源编号。[来源](https://php.net/integer) - thomasrutter

2

我在使用multi_curl时,使用了这个函数,它非常有效地进行排序,以确保文本不会随意排列:

function get_resource_id($resource) {
    if (!is_resource($resource))
        return false;

    return array_pop(explode('#', (string)$resource));
}

0
我建议您创建一个集合对象,而不是使用 $map 变量,例如:
class ResourceCollection implements ArrayAccess {

  private $map = array();

  /**
   * returns index of element or FALSE if not existent
   */
  protected function getIndex($offset){
    if(is_resource($offset)){
      $index = array_search($offset, $this->map);
    }
    else // you can add more tests if you need
      if(isset($this->map[$offset]))
        $index = $offset;
      else
        $index = false;
    return $index;
  }

  /**
   * required by ArrayAccess interface
   */
  public function offsetExists($offset){
    return ($this->getIndex($offset) === false)? false : true;
  }



  /**
   * required by ArrayAccess interface
   */
  public function offsetGet($offset){
    $index = $this->getIndex($offset);
    if($index === false)
      throw new ... // or handle error of non-existent element
    return $this->map[$index];
  }

// etc., implement ArrayAccess interface, Iterator and anything you want
}

尽管我还没有测试过它,但这应该允许您像访问数组一样访问对象,并且我希望通过这种方式(没有针对此的文档),资源可以用作数组索引。


谢谢您的回答,但是array_search()使用迭代,因此这是一个O(n)的解决方案,而我正在寻找的是O(1)的解决方案。 - FtDRbwLXw6
你可以将两个解决方案结合起来,这样你就不必考虑两个变量了 :-) - Voitcus

0

我知道这个问题很老了,但是今天你也可以使用linked-hash-map并将资源用作数组键。

你可以使用Composer进行安装:

composer require tonix-tuft/linked-hash-map

然后像这样使用:

<?php

// ...

use LinkedHashMap\LinkedHashMap;

$map = new LinkedHashMap();

$foo = socket_create(...);
$bar = socket_create(...);

$map[$foo] = 'foo';
$map[$bar] = 'bar';

echo $map[$foo]; // "foo"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接