Blue Static

Bending PHP to Your Will

Posted on August 6, 2011 at 23:33 UTC, filed under the category Uncategorized. Tags: PHP,

One of the best features of Objective-C is protocols. These are akin to PHP and Java interfaces, but have one huge advantage: you can mark certain methods as required or optional. This is useful in implementing the delegate pattern because if you do not care about a certain method, you can simply not implement that method. In PHP, this is not possible with interfaces: you must implement all the methods of an interface in order to implement the interface. While this is fine and makes sense in most cases, it’s not always convenient.

So I set off to find a way to create protocols in PHP. The language is fairly dynamic, so I was able to come up with a solution called WeakInterface. Before jumping into the implementation details, let’s see how it works in practice:

First you define your interface as you would normally:

interface AllOptionalInterface {
  public function DoSomething();
  public function DoSomething1(Closure $arg);
}

The class that implements this only cares about the DoSomething method, so it implements it:

class AllOptionalImpl {
  public function DoSomething() {
    echo 'I did something!';
  }
}

Note that this class isn’t said to implement this interface because it does not implement all the methods. Now in the code that is going to call this interface, you do the following:

$delegate = new WeakInterface('AllOptionalInterface');
$delegate->Bind(new AllOptionalImpl);
$delegate->DoSomething();
$delegate->DoSomething1(function() { echo 'Wow!'; });

This code will output “I did something!”. Let’s go line-by-line. First, $delegate is being created as an instance of WeakInterface, whose constructor takes the name of the interface to “implement.” Then the interface is bound to an instance of the implementation. And then finally two methods are called on the interface, and the one with an implementation is actually called. Calling DoSomething1() is a no-op.

I mentioned above that in Objective-C you can mark some methods as required, too. And that’s done with a docstring when using WeakInterfaces:

interface OneRequiredInterface {
  public function DoSomething();
  /** @required */
  public function DoAnything();
}

If you failed to implement DoAnything(), the call to WeakInterface::Bind() would throw an exception. But what if the number and type hints of the arguments of an implementation do not match that of the interface? WeakInterface has that base covered, too. Bind() will also check the parameters of all method implementations to make sure that the signatures match the interface’s.

That’s WeakInterface in a nutshell, but how does it actually work? It’s easiest to start with a callstack. So when $delegate->DoSomething() is called, this is how WeakInterface actually invokes the implementation:

#	Time	Memory	Function	Location
1	0.0033	975328	hoplite\base\WeakInterface->DoSomething( )	../example.php:14
2	0.0033	975856	hoplite\base\WeakInterface->__call( )	../weak_interface.php:0
3	0.0033	975856	hoplite\base\internal\MethodImp->Invoke( )	../weak_interface.php:84
4	0.0033	976568	ReflectionMethod->invokeArgs( )	../weak_interface.php:145
5	0.0033	976608	AllOptionalImpl->DoSomething( )	../example.php:0

WeakInterface implements the magic PHP method __call(), which is invoked whenever a method call is performed and no method is found by the runtime. It captures this and then forwards the invocation on to an implementation helper MethodImp, which then uses reflection to finally invoke the proper implementation.

Going deeper, when a WeakInterface is constructed, the interface passed to it is reflected and the method list is walked. For each method, a MethodImp is created. When the WeakInterface is bound, that set of MethodImps is looped over, checking that any required methods are implemented and that any implementations match the interface’s method signature. At call time, the method name is looked up in the MethodImp table and the arguments are forwarded to the actual implementation.

One note on performance: due to the number of intermediate method calls, using WeakInterface is roughly four times slower than a plain method call. For most applications, this should not be significant at all; but for performance-critical code, you should steer away from WeakInterface.

The code is available here along with a unit test.

MacGDBp 1.4.1 Released

Posted on April 29, 2011 at 04:58 UTC, filed under the category MacGDBp. Tags: Cocoa, mac os x, MacGDBp,

MacGDBp 1.4.1 has been released; this is a maintenance update that fixes a few bugs. I meant to get this release out a few weeks ago, but I’ve been travelling recently and didn’t have the time.

You can download MacGDBp 1.4.1 from this page. Please file any new issues in the bug tracker.

Tracking Info:
Current SHA1: 7fde123
Version: 1.4.1.91.97
Last Release Build: 2011-04-29 00:33:16

MacGDBp 1.4 Released [u]

Posted on February 26, 2011 at 18:08 UTC, filed under the category MacGDBp. Tags: mac os x, MacGDBp,

After a slight delay trying to track down some bugs, I’ve decided to push MacGDBp 1.4 to stable distribution. While this release is not quite perfect and there are a few known issues (listed on the project page), I was tired of blocking the release on relatively minor bugs. This release has been a year in the making, and it makes significant improvements over the 1.3 branch. To find out more about what went into making this new version, check out some of the older posts on the blog.

You can download MacGDBp 1.4 from this page. Please file any new issues in the bug tracker.

Update: A minor bug had to be fixed with the released package. I’ve pushed a new binary and the few people who have downloaded the update will be prompted to update again.

Tracking Info:
Current SHA1: 4ce0d31 d746f73e
Version: 1.4.0.85 .83
Last Release Build: 2011-02-26 14:21:17 2011-02-26 12:29:16

MacGDBp 1.4 Beta 2

Posted on January 15, 2011 at 05:26 UTC, filed under the category MacGDBp. Tags: Cocoa, mac os x, MacGDBp,

I’m happy to announce MacGDBp 1.4 Beta 2. It’s been nearly a month since the first beta was released and more than 200 people have tested Beta 1. I haven’t received any critical bug reports, which means that the internals are stable enough to ship. Beta 2 is the Release Candidate; barring any significant issues, this will be promoted to Stable in a month or less.

This release has a couple of bug fixes and adds two small enhancements:

  • #210, #195 Add a “Stop” button to detach the debugger from the current session
  • #209 Drag a file onto the source view in the Breakpoints window to load the contents

You can download MacGDBp 1.4 Beta 2 from this page. Please file any new issues in the bug tracker.

Tracking Info:
Current SHA1: 5cfd0db
Version: 1.4.0.74 β2
Last Release Build: 2011-01-14 23:48:03

MacGDBp 1.4 Beta 1

Posted on December 18, 2010 at 22:17 UTC, filed under the category MacGDBp. Tags: mac os x, MacGDBp,

I’m happy to announce the first beta of MacGDBp 1.4. This release represents nearly a year’s worth of work rewriting the entire internal structure of the application. As mentioned previously, MacGDBp 1.4 now communicates to the backend in an asynchronous manner. Not only does this change increase the robustness of the program, it should no longer beachball while waiting for responses from Xdebug. Because it took such a long time to stabilize this branch, there is very little in the way of new features for this release: almost all the work for this release was done for making the network changes and increasing stability.

One new point to be aware of is that the concept of “reconnect” is gone in this version. Instead, the debugger is now either attached to the backend or is detached. When attached, MacGDBp will start debugging any connections from Xdebug. When detached, MacGDBp will send the “detach” command immediately after receiving any new connections, aborting the debug.

There is one known issue:

  • The expanded and selected item state of the variables tree is not preserved across debugger commands (bug #208)

You can download MacGDBp 1.4 Beta 1 from this page. Please try out this new version and file any new issues in the bug tracker.

Tracking Info:
Current SHA1: 1ffc93e
Version: 1.4.0.61 β1
Last Release Build: 2010-12-18 14:18:34

« Older Entries Newer Entries »