Categories


Loading feed
Loading feed

Com_dotnet


Introduction
What's Gone
Iterators
Exceptions
Variants
ByRef parameters
Event Handling
.Net Support
Summary
About the Author

Introduction

The new OO features in PHP 5 are not limited to private, protected and public variables in your scripts - some significant changes have been made under the hood that allow extensions to really hook into the engine and allow much greater flexibility for integrating support for external object models such as COM, Java, .Net and Corba. It was an excellent opportunity to revitalize COM support, as it was suffering from the limitations of the PHP 4 object model - limitations that had resulted in some hacks, both in the extension code itself and in the scripts of people using the extension. With that in mind, I decided to rewrite the COM support from scratch and drop backwards compatibility for the uglier aspects of the PHP 4 COM extension.

What's Gone

The following functions/features are not present in the PHP 5 COM support, as they are nasty hacks that really don't fit with the COM idea, and are really quite confusing things when you see them in the manual.

com_addref(), com_release() - your script should not worry about refcounts

com_get(), com_set(), com_invoke(), com_propget(), com_propset(), com_propput() - just use regular PHP OO syntax to get or set properties or invoke methods.

com_isenum() and $com->Next() - use the foreach() syntax described below instead.

com_load() - use the new operator and the COM class instead.

So, all the nasty stuff has been blasted away. Let's now look at the goodies that have been added.

Iterators

If you've ever dealt with VBscript code, either from an ASP site or in .vbs admin scripts, you're bound to have seen code like this:

<%
set domainObject
= GetObject("WinNT://Domain")
for
each obj in domainObject
  Response
.write obj.Name & "<br>"
next
%>

In PHP 4 COM, the equivalent code looks like this:

<?php
$domainObject
= new COM("WinNT://Domain");
while (
$obj = $domainObject->Next()) {
  echo
$obj->Name . "<br>";
}
?>

If you've done a lot of VB/COM hacking, looking at that code snippet will leave you wondering what exactly the Next() thing is doing; it is not a real method, and you certainly can't do that same thing in other languages that support COM. In PHP 5, this syntax has been dropped in favour of the much more natural foreach() statement:

<?php
$domainObject
= new COM("WinNT://Domain");
foreach (
$domainObject as $obj) {
  echo
$obj->Name . "<br>";
}
?>

Exceptions

In PHP 4, there was no sane way to handle errors triggered from within COM code - when an error occurred, PHP would raise an E_WARNING so that you would know that there was an error, but you would have no good way to know precisely where the error occurred, nor be able to handle it programmatically.

PHP 5 introduces structured exception handling (try, catch() and throw()) and this allows us to expose the underlying COM exceptions to PHP using the built-in com_exception class. If you want to catch errors in your scripts, you might write code like this:

<?php
$com
= new COM("...");
try {
  
$com->call_a_method();
}
catch (com_exception $e) {
  print
$e . "\n";
}
?>

Inside the catch block you have the opportunity to handle the error as is appropriate to your script. The com_exception class extends the default exception class provided by PHP, and so it has all of its methods. The COM exception/error code is made available via the getCode() method of the class, making it drastically easier to handle specific errors.

Variants

One of the not very well documented features of COM in PHP 4 is its support for the VARIANT type. If you're not familiar with variants, you can think of them as the COM equivalent of the PHP variable - structures that can hold integer, floating point, string, or object values. The COM variant has much greater diversity than the PHP variable, and not all of its types can be directly expressed as native PHP types.

The variant support in PHP 4 relied on converting variants back and forth between COM and PHP which resulted in some nasty looking code in the extension to handle the conversions. It was buggy and did not handle all of the possible conversion cases. It was also possible that you would lose information/precision as conversions are made.

In PHP 5, the variant support has been greatly simplified by adopting the premise that we should only convert a variant value to a PHP type when there is a direct 1:1 mapping. In all other cases, we represent the variant as an overloaded object and defer evaluation of it until it is used in an expression. This results in much cleaner code and, if you are working with variant arrays, faster scripts as we no longer need to copy the contents a variant array and put them into a PHP array - the new OO model in PHP 5 allows us to access that variant object as though it were an array.

Another neat trick made possible by the new OO model is smart interpretation of the contents of a variant object using a cast handler. As I mentioned above, we defer evaluation of the variant until it is used in an expression - when it is used, the Zend Engine has some idea of the context in which it is being used (numeric, string etc.) and will ask the variant object to convert itself into an appropriate format. This is particularly useful for us to decide exactly how to convert the value.

If none of that was enough, a large number of the COM variant API functions have been added to the extension, allowing you to add, subtract, multiply, etc. variants according to the same rules used by VB. While not overly useful for the common basic types (integers and strings), it is useful for the more exotic variant types (dates, currency values and so on).

ByRef parameters

It is quite common to encounter a COM object with methods that expect their parameters to be passed by reference. In PHP 4, the only way to make these methods work as expected was to manually create an instance of a VARIANT and set its ByRef flag. The OO model in PHP 5 allows the engine to query COM about the method that it is about to call so that it can determine which parameters are to be passed by reference. This allows you to call these methods without jumping through hoops - the values are set for you automagically.

Event Handling

Not strictly new for PHP 5 (I added it in PHP 4.3), it is worth mentioning here since it is not particularly well documented. Quite often you need to bind to a COM object so that you can receive notification of events - using VB you would use the WithEvents clause when you Dim the variable and then Visual Basic would handle the magic. In PHP things are a little different, although much easier to follow.

To be able to handle events from a COM object (known as a "source") you need to have another object that can "sink" those events. To do this, you simply declare a class to act as your sink and create an instance of it, and then bind the events to it:

<?php
class IESink {
    public
$terminated = false;
    public function
OnQuit() {
        
$this->terminated = true;
    }
}

$ie = new COM("InternetExplorer.Application");
$ie->Visible = true;
$ie->Navigate("http://www.php.net/");

$sink = new IESink;
com_event_sink($ie, $sink, "DWebBrowserEvents2");
while (!
$sink->terminated) {
    
com_message_pump(4000);
}

print
"finished!\n";
?>

This script will launch IE and browse to the PHP home page and then wait for you to quit the browser before continuing. You should note that the com_event_sink() function is responsible for sinking events from $ie to $sink and that it uses an interface named "DWebBrowserEvents2". The interface name must match the name of the so-called outgoing dispinterface for your COM object - you can find out the name and also generate a template sink class using the com_print_typeinfo() function.

.Net Support

PHP 5 includes integrated .Net support. To be more specific, it supports the instantiation of objects defined in .Net assemblies via the COM interoperability layer for .Net. In implementation terms, PHP sees .Net objects as though they were COM objects, although instantiation is slightly different:

<?php
  $stack
= new DOTNET("mscorlib", "System.Collections.Stack");

  
$stack->Push(".Net");
  
$stack->Push("Hello ");

  echo
$stack->Pop() . $stack->Pop();
?>

This provides you with convenient access to the very extensive .Net class library (it has several thousand different classes!). Obviously, to use this feature you need to have installed the .Net runtime onto your server.

In Summary

Although each feature I've mentioned above doesn't sound like much on its own, the total effect will make a massive difference if you have ever written a COM enabled PHP script of decent length - you will find that not only is your script shorter and easier to read and understand, but that it is a little bit faster too.

You can play with these all these features right now by downloading a PHP 5 snapshot from http://snaps.php.net. PHP 5 is currently in feature-freeze, which means that we are focusing on stabilizing the code ready for release - it is already fairly stable, so you are encouraged to try it out for yourself. If you run into problems, please report them using http://bugs.php.net.

About the Author

Wez Furlong is a Core Developer of PHP and "King" of PECL (The PHP Extension Community Library), having contributed extensions such as SQLite, COM/.Net, ActivePHP, mailparse, the Streams API and more.


Comments


Tuesday, May 15, 2007
CAN CONSTRUCTOR PARAMETERS BE PASSED USING DOTNET?
11:05AM PDT · tcarnes
Thursday, May 17, 2007
DOTNET SUPPORT NOT WORKING
8:05AM PDT · jcrutchfield
RE: DOTNET SUPPORT NOT WORKING
12:50PM PDT · tcarnes
Friday, May 18, 2007
RE: DOTNET SUPPORT NOT WORKING
1:59PM PDT · jcrutchfield
Sunday, May 20, 2007
SYSTEM.WINDOWS.FORMS.FORM->CONTROLS
4:34PM PDT · troelskn
Thursday, November 29, 2007
HANDLING COM POINTERS QUESTION
8:22AM PST · alex-party