390 likes | 406 Views
PHP and COM. Wez Furlong < wez@messagesystems.com >. 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.
E N D
PHP and COM Wez Furlong<wez@messagesystems.com>
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