【问题标题】:Using resources as array indexes in PHP在 PHP 中使用资源作为数组索引
【发布时间】:2013-07-20 12:25:25
【问题描述】:

我目前正在使用以下散列方法对resources 进行查找:

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

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

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

integer 是否是最好的选择?如果不是,还有什么其他散列方法会更好或更有效?这些查找将在一个紧密循环(套接字轮询)中以每秒数百次的方式在一个集合中执行,因此我已经排除了基于迭代的解决方案。

编辑:

为了更好地解释我的情况,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...
}

【问题讨论】:

    标签: php arrays hash


    【解决方案1】:

    casting resources to integer 未定义时观察到的行为(请参阅页面底部的注意事项)。所以即使这个works now and reliably did for a long time,你也必须意识到,你不能在没有通知的情况下改变。

    澄清后编辑:

    不要将资源作为键,而是使用两个数组。一种将 Socket 对象的哈希映射到实际对象。另一个将相同的哈希映射到资源。然后将后一个数组传递给socket_select。在函数不会改变数组key的前提下,然后可以迭代数组,用key在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();它可能只是工作。稍后我将不得不对此进行测试。我会跟进并告诉你。谢谢!
    • 我刚刚对此进行了测试,通过socket_select() 调用保留了密钥,一切正常。我已经接受了您的回答并开始了赏金(在我奖励之前 24 小时),因为我非常感谢您的努力和帮助。谢谢!
    • 注意:将资源转换为整数不再是未定义的,如链接的手册页中所示。 If a resource is converted to an integer, then the result will be the unique resource number assigned to the resource by PHP at runtime.source
    【解决方案2】:

    我将此函数与 multi_curl 一起使用,非常有效地进行排序以确保文本不会随机排列:

    function get_resource_id($resource) {
        if (!is_resource($resource))
            return false;
    
        return array_pop(explode('#', (string)$resource));
    }
    

    【讨论】:

      【解决方案3】:

      我建议你创建一个集合对象而不是$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) 解决方案。
      • 您可以将这两种解决方案结合起来,这样您就不必考虑两个变量 :-)
      【解决方案4】:

      我知道这个问题很老了,但从今天开始,您还可以使用 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"
      

      【讨论】:

        猜你喜欢
        • 2021-12-25
        • 2014-05-13
        • 1970-01-01
        • 1970-01-01
        • 2016-07-18
        • 2019-09-18
        • 1970-01-01
        • 2020-01-02
        • 2014-04-29
        相关资源
        最近更新 更多