Get the reference count of an object in PHP?

后端 未结 5 2252
终归单人心
终归单人心 2021-01-01 18:26

I realize the knee-jerk response to this question is that \"you dont.\", but hear me out.

Basically I am running on an active-record system on a SQL, and in order to

相关标签:
5条回答
  • 2021-01-01 19:04

    It seems like the best answer was still getting the reference count, although debug_zval_dump and ob_start was too ugly a hack to include in my application.

    Instead I coded up a simple PHP module with a refcount() function, available at: http://github.com/qix/php_refcount

    0 讨论(0)
  • 2021-01-01 19:05

    I know this is a very old issue, but it still came up as a top result in a search so I thought I'd give you the "correct" answer to your problem.

    Unfortunately getting the reference count as you've found is a minefield, but in reality you don't need it for 99% of problems that might want it.

    What you really want to use is the WeakRef class, quite simply it holds a weak reference to an object, which will expire if there are no other references to the object, allowing it to be cleaned up by the garbage collector. It needs to be installed via PECL, but it really is something you want in every PHP installation.

    You would use it like so (please forgive any typos):

    class Cache {
        private $max_size;
        private $cache = [];
        private $expired = 0;
    
        public function __construct(int $max_size = 1024) { $this->max_size = $max_size; }
    
        public function add(int $id, object $value) {
            unset($this->cache[$id]);
            $this->cache[$id] = new WeakRef($value);
    
            if ($this->max_size > 0) && ((count($this->cache) > $this->max_size)) {
                $this->prune();
                if (count($this->cache) > $this->max_size) {
                    array_shift($this->cache);
                }
            }
        }
    
        public function get(int $id) { // ?object
            if (isset($this->cache[$id])) {
                $result = $this->cache[$id]->get();
                if ($result === null) {
                    // Prune if the cache gets too empty
                    if (++$this->expired > count($this->cache) / 4) {
                        $this->prune();
                    }
                } else {
                    // Move to the end so it is culled last if non-empty
                    unset($this->cache[$id]);
                    $this->cache[$id] = $result;
                }
                return $result;
            }
            return null;
        }
    
        protected function prune() {
            $this->cache = array_filter($this->cache, function($value) {
                return $value->valid();
            });
        }
    }
    

    This is the overkill version that uses both weak references and a max size (set it to -1 to disable that). Basically if it gets too full or too many results were expired, then it will prune the cache of any empty references to make space, and only drop non-empty references if it has to for sanity.

    0 讨论(0)
  • 2021-01-01 19:09

    Yes, you can definitely get the refcount from PHP. Unfortunately, the refcount isn't easily gotten for it doesn't have an accessor built into PHP. That's ok, because we have PREG!

    <?php
    function refcount($var)
    {
        ob_start();
        debug_zval_dump($var);
        $dump = ob_get_clean();
    
        $matches = array();
        preg_match('/refcount\(([0-9]+)/', $dump, $matches);
    
        $count = $matches[1];
    
        //3 references are added, including when calling debug_zval_dump()
        return $count - 3;
    }
    ?>
    

    Source: PHP.net

    0 讨论(0)
  • 2021-01-01 19:18

    Sean's debug_zval_dump function looks like it will do the job of telling you the refcount, but really, the refcount doesn't help you in the long run.

    You should consider using a bounded array to act as a cache; something like this:

    <?php
    class object_cache {
       var $objs = array();
       var $max_objs = 1024; // adjust to fit your use case
    
       function add($obj) {
          $key = $obj->getKey();
          // remove it from its old position
          unset($this->objs[$key]);
          // If the cache is full, retire the eldest from the front
          if (count($this->objs) > $this->max_objs) {
             $dead = array_shift($this->objs);
             // commit any pending changes to db/disk
             $dead->flushToStorage();
          }
          // (re-)add this item to the end
          $this->objs[$key] = $obj;
       }
    
       function get($key) {
          if (isset($this->objs[$key])) {
              $obj = $this->objs[$key];
              // promote to most-recently-used
              unset($this->objs[$key]);
              $this->objs[$key] = $obj;
              return $obj;
          }
          // Not cached; go and get it
          $obj = $this->loadFromStorage($key);
          if ($obj) {
              $this->objs[$key] = $obj;
          }
          return $obj;
       }
    }
    

    Here, getKey() returns some unique id for the object that you want to store. This relies on the fact that PHP remembers the order of insertion into its hash tables; each time you add a new element, it is logically appended to the array.

    The get() function makes sure that the objects you access are kept at the end of the array, so the front of the array is going to be least recently used element, and this is the one that we want to dispose of when we decide that space is low; array_shift() does this for us.

    This approach is also known as a most-recently-used, or MRU cache, because it caches the most recently used items. The idea is that you are more likely to access the items that you have accessed most recently, so you keep them around.

    What you get here is the ability to control the maximum number of objects that you keep around, and you don't have to poke around at the php implementation details that are deliberately difficult to access.

    0 讨论(0)
  • 2021-01-01 19:27

    PHP 7.4 now has WeakReference

    To know if $obj is referenced by something else or not, you could use:

    // 1: create a weak reference to the object
    $wr = WeakReference::create($obj);
    
    // 2: unset our reference
    unset($obj);
    
    // 3: test if the weak reference is still valid
    $res = $wr->get();
    if (!is_null($res)) {
        // a handle to the object is still held somewhere else in addition to $obj
        $obj = $res;
        unset($res);
    }
    
    0 讨论(0)
提交回复
热议问题