PHP COM class: consuming .Net from PHP

PHP COM class: consuming .Net from PHP

Difficulty: 
Piece of Cake

The COM class is part of the COM_DOTNET extension that is part of the PHP core.

COM class allows you to instantiate an OLE compatible COM object and call its methods and access its properties. You can use this technique to call .Net Framework assemblies or COM components such as Word, Excel and other Microsoft Office tools.

In this guided example you will learn how to call the Microsoft Word interop component from PHP, but you can use this same procedure with any .Net or COM type on your system.

Enabling the COM_DOTNET extension

The first thing you need to do is enable the official Core extension COM_DOTNET that comes bundled with PHP Core. Add this to your php.ini:

[PHP_COM_DOTNET]
extension=php_com_dotnet.dll

To make sure that the extension ir properly enabled and configured use phpinfo():

Registering the NetPhp binary

Next we are going to register the NetPhp COM component. Download and extract the netutilities.dll from this link.

Once downloaded place the DLL file somewhere in your system that you will not forget about such as:

D:\netphp\netutilities.dll

Next we are going to use the regasm tool to register the COM component:

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe" d:\netphp\netutilities.dll /codebase

Adding the NetPhp classes to your project

The next step is to add the NetPhp classes to your current or new PHP project. The easiest way to accomplish this is using composer.

We will be also using composer to manage autoloading for the PHP class model what will be generated in order to interact with .Net, so we will define that namespace here ("wd") and another namespace for our application code ("MyProject"):

{
    "require": {
        "drupalonwindows/netphp": "2.x-dev"
    },
    "autoload": {
        "psr-4": {
            "MyProject\\": "src/",
            "wd\\": "wd/"
        }
    }
}

Run composer install or composer update to retrieve the packages into your project and to generate the required autoload files.

Understading how composer and autoloading works in PHP is crucial to building succesful PHP projects, you can read this from the official documentation for more details.

Preparing the NetPhp runtime

The next step is to tell NetPhp what libraries your are going to use. To do so we instantiate a NetPhpRuntime class and register our assemblies.

// Instantiate and initialize the Model
$this->runtime = new \NetPhp\Core\NetPhpRuntime();
$this->runtime->Initialize();

// Add the full qualified name.
$this->runtime->RegisterAssemblyFromFullQualifiedName('Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c', 'Word');

// We check if it exists because we might not have yet dumped
// any class model.
if (class_exists(\ms\TypeMap::class)) {
  \wd\TypeMap::SetRuntime($this->runtime);
}

If you don't know the assembly full qualified name you can use the name of an Interop type to let the runtime look for the assembly itself:

$this->runtime->RegisterAssemblyFromProgId("word.application");

Dumping the static PHP model

Operating with a COM class from PHP without a PHP class model is very error prompt because PHP (and your IDE) have no knowledge of what the objects in the Interop types look like. To fill that gap the NetPhp library allows you to dump a PHP class model so that you can interact with the Interop types as if they where PHP classes.

Use this code to dump the PHP model. Make sure that the namespaces and paths match properly what has been registered for autoloading in your composer.json file.

// Initialize the dumper.
$dumper = new \NetPhp\Core\TypeDumper();
$dumper->Initialize();

// If you don't call this, and the destination directory is not empty
// the static class model will not be generated. Just a safe guard.
//$dumper->AllowDestinationDirectoryClear();

// Set the destination path and base namespace.
// Al the .Net namespaces will be nested inside this
// namespace.
$dumper->SetDestination(APPLICATION_ROOT . '/wd');
$dumper->SetBaseNamespace('wd');

// Get a copy of the runtime.
$runtime = $this->GetRuntime();

// Tell the runtime to register the assemblies in the Dumper.
$runtime->RegisterAssembliesInDumper($dumper);

// Dump the complete SpreadshetLight namespace.
// You need to explicitly add one or more regular expressions
// that will thell the dumper what Types to consider
// when dumping the model.

// This to dump EVERYTHING
$dumper->AddDumpFilter('.*');

// Limit the depth of Type recursive discovery. From the classes
// that you have just filtered, the dumper will start recursively
// detecting what other types your types depend on (interfaces,
// base classes, parameters, etc.) and include them in the Dump.
$dumper->SetDumpDepth(1);

// Generate the static class model.
try {
  $dumper->GenerateModel();
  // Just make sure that PHP can start using this new classes
  // during this same request.
  clearstatcache();
}
catch (\Exception $e){}

Once dumped (might take a while) you will have a complete set of PHP classes that are visible to your IDE and will make your life much easier when dealing with the Interop types.

 

Consuming the PHP class model

You are now ready to start consuming your dumped class model:

    // Make sure the Runtime is Initialized.
    RuntimeManager::Instance()->InitializeRuntime();

    // COM EXAMPLE #1 FROM http://php.net/manual/en/class.com.php
    $word = \wd\Microsoft\Office\Interop\Word\netApplicationClass::ApplicationClass_Constructor();

    echo "Loaded Word, version {$word->Version()->Val()} </br>";

    $word->Visible(TRUE);

    // The add method is not visible in the PHP model
    // because it contains reference parameters, but you can still
    // use it.
    $word->Documents()->Add();

    $word->Selection()->TypeText("This is a test...");
    
    $word->Quit();

    $word = NULL;

All of the code, with a working example can be found in the NetPhp sample project in Github.

An in-depth guide on consuming .Net from PHP can be found here.

Add new comment

By: root Thursday, October 29, 2015 - 09:11