SilverStripe on HHVM: Part Two

Posted by Jared Kipe on | 1 Comments

Tags: , ,

Magic Method __get() and Zend PHP (with Video)

The SilverStripe object hierarchy makes extensive use of PHP's __get() magic method. So much so that you probably cannot edit an article on HHVM right now. Unfortunately, currently this means you will need to edit and re-compile the HHVM source before it will get any better.

The Bug

Here is a simple demonstration file/class to run on both Zend and HHVM PHP.


<?php
class Test {
	protected $record = array('ID' => 1);
		
	public function __get($property) {
		if(method_exists($this, $method = "get$property")) {
			return $this->$method();
		} 
		return $this->record[$property];
	}
		
	protected function getName() {
		return "Database->getNameForId({$this->ID})";
	}
		
}
	
$t = new Test();
echo 'ID: ' . $t->ID . "\n";
echo 'Name: ' . $t->Name . "\n";

What Zend PHP (e.g. 5.5.5) says:

You are not allowed to use the 'same' magic property name twice.

No surprise, the output is ...

ID: 1
Name: Database->getNameForId(1)

What HHVM (2.2.0 or master) says:

From hhvm/hphp/doc/bytecode.specification:

Magic property access methods
-----------------------------
Instructions that access properties may in some cases invoke a magic property access method (__get, __set, __isset, or __unset) if an object implements the
method and the method is considered eligible for invocation. A magic property
access method is considered "eligible" for a given object if there is not a
frame on the call stack that corresponds to an invocation of the same method on
the same object.

This means that once an object enters into __get, that object cannot enter into __get again until it returns from the original __get.

ID: 1
Name: Database->getNameForId()

This would probably make some invalid SQL or throw an exception. For SilverStripe this is a big problem. SilverStripe uses __get() in some cool ways, for example lazy loading object data.

HHVM is attempting to push for parity, and fixing this is ultimately a parity issue.  Unfortunately for HHVM, the current implementation of magic method recursion protection is fast, but very incompatible with what would need to happen to actually achieve parity. (Zend uses hash maps to tell if a specific propery has already been requested through __get instead of just if __get is used somewhere up the call stack.

Editing the HHVM Source to Fix/Hack

As long as you can compile HHVM, you can fix this yourself today (hopefully HHVM will fix this on their own for some future release).

hhvm/hphp/runtime/base/object-data.h


// line ~215
//  bool getAttribute(Attribute attr) const { return o_attribute & attr; } 
  bool getAttribute(Attribute attr) const {
      if (attr == UseGet) {
          return true;
      }
      return o_attribute & attr;
  }

This will make __get() 'always' eligible, and allow SilverStripe to get away with the complicated __get method that we all love.


Post your comment

Comments

  • Gravatar for Mark Guinn

    Jared, I just installed silverstripe on a bleeding edge version of hhvm (via puphpet) and it appears this bug has been fixed. I ran the test script above and it now gives the same output as the zend version.

    I think they've now added a mysqli driver too because my database just worked out of the box.

    Posted by Mark Guinn, 25/08/2014 5:59am (3 years ago)

RSS feed for comments on this page | RSS feed for all comments