php and com
DESCRIPTION
A discussion of the OLE/COM bridge in PHP 5. These slides are from 2004. I\'m hoping that SlideShare preserves the speakers notes; there\'s a good deal of information in there to complement the slide content.TRANSCRIPT
Plan
• What is COM?
• What's it good for?
• How do I use it?
• Instantiation... and Monikers
• Exceptions
• Typelibraries
• Variants
• .Net Interop
• ActiveScript
• Persistence (experimental)
Common Object Model
• Frequently mislabeled asComponent Object Model
• Specifies programming interfaces for OO code
• Those interfaces are programming language independent
• Provides a mechanism for code re-use
OLE
• A subset of these interfaces are collectively known as OLE:Object Linking and Embedding
• They define a way to dynamically create and invoke methods on objects that implement those interfaces
• PHP COM is really PHP OLE
COM Servers
• A COM Server is some module that exposes a COM interface via creatable classes
• In-Proc (DLL) => fast
• Out-of-Proc (EXE) => not so fast
• DCOM => on another machine
• Your code sees these as the same thing
What's it good for?
• Talking to other applications and libraries
• Most win32 software vendors provide a COM interface
• Can be used to transition from ASP to PHP
• Can use “easy” languages to extend PHP
How do I use it?
$word = new COM(“Word.Application”);$word->visible = true;$word->Documents->Add();$word->Selection->TypeText(“hello”);$word->ActiveDocument->SaveAs(“test.doc”);$word->Quit();
Instantiation
$word = new COM(“Word.Application”, array( ‘Server’ => ‘otherbox’, ‘Username’ => ‘foo’, ‘Password’ => ‘bar’ ), CP_UTF8);
Exceptions
try { $o = new COM(“…”);} catch (com_exception $e) { print “failed to create: $e”;}
$e->getCode() corresponds to weird hex number (will show example later)
Typelibraries
• Import constants from a COM server as regular PHP constants.
• Manually
• com_load_typelib(‘Word.Application’);
• echo wdGoToBookmark;
• From php.ini:
• com.typelib_file=C:\mytypelibs.txt
Variants
• PHP COM is really PHP Variant
• Variant is win32 equivalent of PHP typeless variables
• JIT conversion in PHP 5
• OLE default properties
• $doc = $word->ActiveDocument;
• print $doc; // prints document title
Variant Arrays
$user = new COM( “LDAP://cn=user,ou=test,dc=php,dc=net”);
$arr = $user->GetEx(“otherHomePhone”);
for ($i = 0; $i < count($arr); $i++) { print $arr[$i] . “\n”;}
Iterators
Set domainObject = GetObject("WinNT://Domain")
For Each obj in domainObject Response.Write obj.Name & "<br />“Next
$domainObject = new COM("WinNT://Domain");
foreach ($domainObject as $obj) { echo $obj->Name . "<br />";}
Variant Functions
• Arithmetic
• variant_add variant_sub
• variant_mul variant_div
• Casting
• variant_set_type (VT_BSTR etc.)
• variant_get_type
• variant_date_from_timestamp
• variant_date_to_timestamp
• VB-like behaviour
.Net Interop
.Net is modern replacement for VB
Uses similar concepts to COM
MS provide an “Interop” layer to map .Net assemblies into COM
$stack = new DOTNET(‘mscorlib’, ‘System.Collections.Stack’);
$stack->push(‘.Net’);
$stack->Push(‘Hello ’);
echo $stack->pop() . $stack->pop();
Some Sample Scripts
• Look at some common tasks implemented in PHP
• Gotchas?
WMI for system monitoring
$wmi = new COM( “WinMgmts:{impersonationLevel=impersonate}” .“//{$hostname}/root/cimv2”);
$cpus = $wmi->ExecQuery( "Select * from Win32_Processor");
foreach ($cpus as $cpu) { printf(“%s %dMHz %d%%\n”, $cpu->Name, $cpu->MaxClockSpeed, $cpu->LoadPercentage);}
ADO DB
Useful for transitioning ASP
Often faster to use odbc or native drivers
$conn = new COM(‘ADODB.Connection’);$conn->Open($dsn);$conn->Execute(‘update …’);
$rs = new COM(‘ADODB.Recordset’);$rs->Open(‘select foo from bar’, $conn);
while (!$rs->EOF()) { echo $rs->Fields(‘foo’); $rs->MoveNext();}
MS Office (Word)
$word = new COM(“Word.Application”);
$word->visible = true;
$word->Documents->Add();
$word->Selection->TypeText(“hello ” . $_SESSION[‘username’]);
$word->ActiveDocument->SaveAs(“test.doc”);
$word->Quit();
Shmop mutex
• Use a single instance of Word
• Use mutex to control access to it
• extension=php_shmop.dll
• Need to be running ISAPI or Apache module on win2k and later
Shmop mutex 2
function try_mutex($timeout) { $t = time(); do { $mtx = shmop_open(42, “cwn”, 0644, 1); if ($mtx) return $mtx; usleep(200000); // 0.2 seconds } while (time() < $t + $timeout); return false;}
function drop_mutex($mtx) { shmop_delete($mtx);}
Shmop mutex 3
com_get_active_object() returns a handle to an instance from the Running Object Table
function get_one() { try { $w = com_get_active_object(‘Word.Application’); return $w; } catch (com_exception $e) { if ($e->getCode() == MK_E_UNAVAILABLE) return new COM(‘Word.Application’); throw $e; }}
Shmop mutex 4
$mtx = try_mutex(3);if (!$mtx) { .. Try again later … }$word = get_one();$word->Documents->Add();$word->Selection->TypeText(“foo”);$word->ActiveDocument->SaveAs($filename);$word->ActiveDocument->Close();drop_mutex($mtx);readfile($filename);
Create a server app
• The shmop idea is prone to failure if something bad happens to a request that holds the mutex
• An alternative is to run a separate process as a tcp server and queue requests to it
• PHP 5 makes this easy
Client for Word Daemon
$daemon = stream_socket_client(‘127.0.0.1:4000’);
if ($daemon) {
fwrite($daemon, serialize($data));
$result = fgets($daemon);
}
Word Daemon
$w = new COM(‘Word.Application’);$s = stream_socket_server(‘127.0.0.1:4000’);while (true) { $client = stream_socket_accept($s); $data = unserialize(stream_get_contents($client)); generate_word_doc($data); fwrite($client, “DONE\r\n”); fclose($client);}
With Events
• COM event handling framework is build from ‘Connection Points’
• A source object is implements IConnectionPointContainer
• A sink object (callback handler) implements a dispinterface
• dispinterfaces allow a loose handler implementation; perfect for scripting languages
turning on events
bool com_event_sink($object,
$sinkobject [, $sinkname]);
Plumbs in event handling;
Events from $object are sunk into $sinkobject
Sinking events from IE
$ie = new COM("InternetExplorer.Application");
$ie->Visible = true;
$ie->Navigate("http://www.php.net");
IE Events 2
class IEEvents { var $dom = null; function DocumentComplete($dom, $url) { echo “$url complete\n”; $this->dom = $dom; }}
IE Events 3
$sink = new IEEvents;$ie = new COM("InternetExplorer.Application");$ie->Visible = true;com_event_sink($ie, $sink, ‘DWebBrowserEvents2’);$ie->Navigate("http://www.php.net");while (!$sink->dom) { com_message_pump(4000);}
// we can do stuff with $sink->dom here, or just continue with something else
IActiveScript
• A set of interfaces that abstract scripting engines
• A compatible host can run any compliant script engine
• Works in both directions; you can load engines in php, and you can load php into other hosts
Invoke JScript from PHP
class foo { function bar($msg) { echo $msg; }}
$js = new COM(‘ScriptControl’);$js->Language = ‘JScript’;$js->AddObject(‘foo’, new foo, false);$js->AddCode(‘foo.bar(“js!”);’);
ActiveScript
• This SAPI lets you load PHP into other scripting engines
• You need to regsvr32 php5activescript.dll to enable it
• Language = PHPScript
• Sadly, can't load PHP into PHP via COM due to architecture of PHP SAPI interface
Using PHP in Windows Script Host
Create a .wsf file like this:
<job id="test"> <script language="PHPScript"> $WScript->Echo("Hello"); </script></job>
cscript test.wsf
Persistence Helper
• Not everything supports the relevant interfaces, so not totally useful
• Intention is to persist object state into streams or strings and store it into some kind of DB.
• PHP streams are mapped as COM IStreams
Bogus Example
$object = new COM(‘…’);$object->doSomething();$dest = fopen(‘ftp://…/…’, ‘wb’);$p = new COMPersistHelper($object);$p->SaveToStream($dest);
Bogus Example 2
$object = new COM(‘…’);$src = fopen(‘ftp://…/…’, ‘rb’);$p = new COMPersistHelper($object);$p->LoadFromStream($src);
// $object is now in same state as it was on previous slide
Resources
• These slides are on my blog and on slideshare.nethttp://netevil.org
• PHP COM manual:http://www.php.net/manual/en/ref.com.php
• WMI:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_start_page.asp
• Server-side MS Office:http://support.microsoft.com/default.aspx?scid=kb;EN-US;q257757
• PHP snapshotshttp://snaps.php.net