(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP
Введение
Класс com позволяет создавать экземпляры OLE-совместимого COM-объекта, вызывать его методы и
получать доступ к его свойствам.
Обзор классов
public __construct(
string $module_name
,
array|string|null $server_name
= null
,
int $codepage
= CP_ACP
,
string $typelib
= «»
)
}
Перегруженные методы
Возвращаемые объекты являются перегруженными, то есть PHP
не видит каких-либо фиксированных методов, как это происходит
с обычными классами. Вместо этого свойства и доступ к методам
передаётся через COM.
PHP автоматически определяет методы, которые
обращаются к свойствам по ссылкам и автоматически преобразуют
стандартные переменные PHP в вид, который можно передавать по ссылке.
Это означает, что вы можете вызывать методы не внося каких-либо
дополнений в свой код.
Примеры использования com
Пример #1 Первый пример
<?php
// запускаем word
$word = new com("word.application") or die("Невозможно создать экземпляр Word");
echo "Загружен Word, версия {$word->Version}n";//делаем его активным окном
$word->Visible = 1;//открываем пустой документ
$word->Documents->Add();//Что то с ним делаем
$word->Selection->TypeText("Это проверка...");
$word->Documents[1]->SaveAs("Бесполезный тест.doc");//закрываем word
$word->Quit();//высвобождаем ресурсы объекта
$word = null;
?>
Пример #2 Второй пример
<?php
$conn
= new com("ADODB.Connection") or die("Cannot start ADO");
$conn->Open("Provider=SQLOLEDB; Data Source=localhost;
Initial Catalog=database; User ID=user; Password=password");$rs = $conn->Execute("SELECT * FROM sometable"); // Набор записей$num_columns = $rs->Fields->Count();
echo $num_columns . "n";
for (
$i=0; $i < $num_columns; $i++) {
$fld[$i] = $rs->Fields($i);
}$rowcount = 0;
while (!$rs->EOF) {
for ($i=0; $i < $num_columns; $i++) {
echo $fld[$i]->value . "t";
}
echo "n";
$rowcount++; // увеличиваем счётчик строк
$rs->MoveNext();
}$rs->Close();
$conn->Close();$rs = null;
$conn = null;?>
Содержание
- com::__construct — Конструктор класса com
tsintra at humansoft dot pt ¶
13 years ago
After one week of trying to understand what was wrong with my PHP communication with MS Word, i finally got it working...
It seems that if you're running IIS, the COM object are invoked with restricted privileges.
If you're having permission problems like not being able to opening or saving a document and you're getting errors like:
- This command is not available because no document is open
or
- Command failed
try this (if you're running IIS):
- Execute "dcomcnfg"
- Open Component Services > Computers > My Computer > DCOM Config
- Search for Microsoft Office Word 97-2003 Document (it will be something like this translated to your language, so take a while and search for it)
- Right-Click on it and open the properties
- Choose "Identity" tab
- Normally this is set to "the launching user". You have to change this to "the interactive user" or a admin user of your choice.
- Apply these new settings and test your COM application. It should work fine now.
I hope I save lots and lots of hours of headaches to some of you :)
rogier ¶
11 years ago
Took me a while to figure this out by trial and error:
If your frustrated when getting a com-exception like "Error [0x80004002] ..." (with a message nagging you about an unsupported interface), and you're calling a class method through COM that expects a char as an argument, you should NOT call the method with a single-character string.
The correct way of calling a method like this is to use a VARIANT with a type of VT_UI1.
COM enabled method definition:
public bool DoSomething(char arg);
<?php
$com
= new COM('Some.Class.Name');// This will fail
$var = 'a';
$com->DoSomething($var);// This works correctly
$var = new VARIANT(ord('a'), VT_UI1);
$com->DoSomething($var);?>
I hope this helps some people
info at ensostudio dot ru ¶
2 years ago
Complete example of using WScript.Shell:
<?php
/**
* Methods & properties
* @link https://msdn.microsoft.com/en-us/library/2f38xsxe(v=vs.84).aspx
*/
$shell = new com('WScript.Shell', null, CP_UTF8);
var_dump($shell->CurrentDirectory);
foreach ($shell->Environment as $value) {
var_dump($value);
}
// check file 'Test.php'
$process = $shell->Exec('.../php.exe --syntax-check --file=".../src/Test.php"');
// wait complete: 1 - done
while (! $process->Status) {
usleep(5000);
}
// results: -1 - syntax error, 0 - no error
var_dump($process->ExitCode);
// show responce
var_dump($process->StdOut->ReadAll());
// show errors
var_dump($process->StdErr->ReadAll());
?>
yinon at xacct dot com ¶
21 years ago
In order to get the Word exaple running, do the following on the server side.
Worked for me...
1. Click START-->RUN and enter "dcomcnfg"
2. In the "Applications" tab, go down to "Microsoft Word Document"
3. Click PROPERTIES button
4. Go to the "Security" Tab
5. Click "Use custom access permissions", and then click EDIT
6. Click ADD and then click SHOW USERS
7. Highlight the IIS anonymous user account (usually IUSR_<machinename>), click ADD
8. Go back to the "Security" tab by hitting OK
9. Click "Use custom launch permissions", and the click EDIT
10. Click ADD and then click SHOW USERS
11. Highlight the IIS anonymous user account (usually IUSR_<machinename>), click ADD
12. Hit OK, and then hit APPLY.
Also, you should look at the "Identity" tab in the Microsoft Word Document PROPERTIES and see that it is set to "Interactive User"
ALSO, log into the machine AS the IUSR_<machinename> account, start word, and make sure to click through the dialog boxes that Word shows the first time it is run for a certain user. In other words, make sure Word opens cleanly for the IUSR_ user.
More useful information could be found here:
http://www.email-screen.com/support-doc2txt.html
mbirth at webwriters dot de ¶
14 years ago
Using the DynamicWrapper from http://freenet-homepage.de/gborn/WSHBazaar/WSHDynaCall.htm and after registering it via "regsvr32 dynwrap.dll", you can easily set output colors to make colorful CLI scripts even on Windows.
<?php
$dw
= new COM('DynamicWrapper'); // needs dynwrap.dll (regsvr32 dynwrap.dll)
// register needed features
$dw->Register('kernel32.dll', 'GetStdHandle', 'i=h', 'f=s', 'r=l');
$dw->Register('kernel32.dll', 'SetConsoleTextAttribute', 'i=hl', 'f=s', 'r=t');
$dw->Register('kernel32.dll', 'SetConsoleTitle', 'i=s', 'f=s', 'r=l');// get console handle
$ch = self::$dw->GetStdHandle(-11); // -11 = STD_OUTPUT_HANDLE?>
After these initialization steps, you can set the console output color using:
<?php
$dw->SetConsoleTextAttribute($ch, 14);
echo 'This is yellow text!';
$dw->SetConsoleTextAttribute($ch, 7);
echo 'Back to normal gray!';
?>
Using SetConsoleTitle you can even set the title displayed in the console window.
Color values are 0..7 for dark colors and 8..15 for light colors. Sequence is (black/silver, blue, green, cyan, red, magenta, yellow/brown, white/gray). I also found that if you add 16384 to the value, it should be inverse. 32768 added should get underlined text. But these latter two didn't work for me.
mastrboy.servebeer.com ¶
14 years ago
quick wmi query example:
<?php
$obj = new COM ( 'winmgmts://localhost/root/CIMV2' );
$wmi_computersystem = $obj->ExecQuery("Select * from Win32_ComputerSystem");
$wmi_bios = $obj->ExecQuery("Select * from Win32_BIOS");
foreach ( $wmi_computersystem as $wmi_call )
{
$model = $wmi_call->Model;
}
foreach (
$wmi_bios as $wmi_call )
{
$serial = $wmi_call->SerialNumber;
$bios_version = $wmi_call->SMBIOSBIOSVersion;
}
echo "Bios version : $bios_versionn".
"Serial number : $serialn".
"Hardware Model : $modeln";
?>
halfer ¶
15 years ago
Thanks to paul at completewebservices who added a note earlier; I have used his code to solve a particularly difficult issue with the Crystal 9 runtime component. For some reason, VBA/VBS can pass a native Date type as a parameter to a Crystal Report requiring a date, but in PHP I've tried all manner of strings, unix timestamps, COM variant dates etc and all of these resulted in a com_exception.
My solution was to employ VBScript to borrow the CDate function, which works correctly. Not the most elegant of solutions, but this parameterised approach is to be preferred to search-n-replacing the RecordSelectionFormula string. Of course if anyone here has an even better approach, do post it here - I am sure it would be of use.
<?php
// $rptParam is a report parameter object having a type of date
$oScript = new COM("MSScriptControl.ScriptControl");
$oScript->Language = "VBScript";
$oScript->AllowUI = false;
$oScript->AddObject('rptParam', $rptParam, true);
$oScript->AddCode('Function SetDateParameter(strDate)
rptParam.AddCurrentValue(CDate(strDate))
End Function');
$oScript->Run("SetDateParameter", "25 April 2006");
?>
djogopatrao at gmail dot com ¶
17 years ago
I found somewhere (http://www.recrystallize.com/merchant/supportfaq/supportfaq0003.htm) a list relating each version of Crystal Reports to its respective progID - it may help some fellows out there.
7 Crystal.CRPE.Application
8.0 CrystalRuntime.Application or CrystalRuntime.Application.8
8.5 CrystalRuntime.Application or CrystalRuntime.Application.8.5
9 (RDC) CrystalRuntime.Application.9
9 (RAS) CrystalReports.ObjectFactory.2
10 (RDC) CrystalRuntime.Application.11
10 (CEE) CrystalReports10.ObjectFactory.1
XI (RDC) CrystalRuntime.Application.11
XI (RAS) CrystalReports11.ObjectFactory.1
james dot m dot love at gmail dot com ¶
14 years ago
If you're using COM to create a DSN Less connection to an MS Access 2007 database, you'll find through Googling that you need to use an updated version of Jet, so the connection string I've found looks like this:
<?php
$databaselocation = "C:Pathtodb.accdb";
$conn = new COM('ADODB.Connection') or exit('Cannot start ADO.');
$conn->Open("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=$databaselocation") or die('Cannot start with Jet');
?>
However, on my setup (WinVista, Access 2007, PHP526) I found that it always 'dies' with "Cannot start with Jet", even if the connection was successful ($conn->State reads "1").
I figured to just drop the die() command on $conn->Open(). If something catastrophic happens with the ADO connection then it's own error handling engine passes stack traces, etc back to the console/standard output.
Therefore, my Open() command looks like this:
<?php
$conn->Open("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=$databaselocation");
?>
csaba at alum dot mit dot edu ¶
18 years ago
Getting IE to the foreground
If you are using a command line (CLI) version of PHP on a Win32 platform (e.g. XP Pro, SP2), you might want to have output directed to IE (perhaps you'll want to work with the output there) when Popup does not suffice (see my earlier post, below).
It's easy enough to get an instance of IE using $ie = new COM("InternetExplorer.Application");. The problem is, you don't necessarily see it in the foreground (especially if you already have one open) and who wants to waste keystrokes getting to it? The code below has been working for me (If you want to do other adjustments (e.g. $ie->Document->ParentWindow->resizeTo(800,500); or $ie->Document->Body->bgColor = "yellow";), doing them before the $ie->Visible = true; line will avoid screen distractions):
<?php
function newIEtoForeground($title, $evtPrefix="") {
// brings new instance of IE to foreground with title $title
if (!$extPrefix) $ie = new COM("InternetExplorer.Application");
else $ie = new COM("InternetExplorer.Application", $evtPrefix);
$ie->Navigate2("about:blank");
$oWSH = new COM("WScript.Shell");
while ($ie->ReadyState!=4) usleep(10000);$ie->Document->Title = ($tmpTitle = mt_rand()); //unique title
$ie->Visible = true;
while (!$oWSH->AppActivate("$tmpTitle - M")) usleep(10000);$ie->Document->Title = $title;
$ie->Document->ParentWindow->opener="me"; // allows self.close()
return $ie;
}
?>
Csaba Gabor from Vienna
rickardsjoquist at hotmail dot com ¶
19 years ago
If you want to use a DSN-less connection to a database (access for example) and return the results of a query as a multidimensional array with the fieldnames as identifier try this. Hope it helps someone :-)
--------------------------------------------------------
<?php
function db($sql) {
$c = new COM("ADODB.Connection");
$c->open('DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=' . realpath("relative_path/db.mdb"));
$r = $c->execute($sql);
$i = 0;
$num_fields = $r->fields->count();
while (!$r->EOF)
{
for($j = 0;$j<$num_fields;$j++) {
$key = $r->Fields($j);
$r_items[$i][$key->name] = $key->value;
}
$i++;
$r->MoveNext();
}
$r->close();
$c->close();
$r = null;
$c = null;
return $r_items;
}
?>
--------------------------------------------------------
use it like this:
--------------------------------------------------------
<?php
$results = db("SELECT field_a, field_b FROM table");
foreach($result as $i => $item) {
echo $item['field_a'];
echo $item['field_b'];
}
?>
--------------------------------------------------------
You can also use: print_r($result); if you want the structure of the array to be printed.
z3n at overflow dot biz ¶
13 years ago
On a query reply using ADODB.Connection object, all my $rs->Fields['field_name']->Value returned `variable Object` when inserted into a array, like this:
<?php
for ($rl[$v]=array(),_qm($_query);!$res->EOF;$res->MoveNext()) {
$rl[$v][]=$res->Fields['x']->Value;
}
?>
I figured out that converting the value into a INT fixed the issue:
<?php $rl[$v][]=intval($res->Fields['x']->Value); ?>
sparrowstail at googlemail dot com ¶
12 years ago
This script reports all Windows drives, drive type, status (if not available), free space and total drive size:
<?php
$fso
= new COM('Scripting.FileSystemObject');
$D = $fso->Drives;
$type = array("Unknown","Removable","Fixed","Network","CD-ROM","RAM Disk");
foreach($D as $d ){
$dO = $fso->GetDrive($d);
$s = "";
if($dO->DriveType == 3){
$n = $dO->Sharename;
}else if($dO->IsReady){
$n = $dO->VolumeName;
$s = file_size($dO->FreeSpace) . " free of: " . file_size($dO->TotalSize);
}else{
$n = "[Drive not ready]";
}
echo "Drive " . $dO->DriveLetter . ": - " . $type[$dO->DriveType] . " - " . $n . " - " . $s . "<br>";
}
function
file_size($size)
{
$filesizename = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB");
return $size ? round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $filesizename[$i] : '0 Bytes';
}
?>
There are a number of other properties of the drive object which can be called this way - see:
http://msdn.microsoft.com/en-us/library/ts2t8ybh%28VS.85%29.aspx
juan156_elias at gmail dot com ¶
7 years ago
This is an example of using .Net using the NetPhp library that is based on the COM/DOTNET classes.
You can use any .dll file, wether it is or not COM decorated and any of the .Net framework types out of the box. You can also target any version of the .Net framework.
<?php
$runtime
= new NetPhpCoreNetPhpRuntime('COM', 'netutilities.NetPhpRuntime');// Add both SpreadsheetLight and the OpenXML it depends on.
$runtime->RegisterAssemblyFromFile('/binaries/SpreadsheetLight.dll', 'SpreadsheetLight');
$runtime->RegisterAssemblyFromFile('/binaries/DocumentFormat.OpenXml.dll', 'DocumentFormat.OpenXml');
$runtime->RegisterAssemblyFromFile('/binaries/AjaxMin.dll', 'AjaxMin');// Using the FullName of a type that belongs to an assembly that has already been registered.
$datetime = $runtime->TypeFromName("System.DateTime");// Using the FullName of a type that has not been registered yet (from a file)
$minifier = $runtime->TypeFromFile("Microsoft.Ajax.Utilities.Minifier", APPLICATION_ROOT . '/binaries/AjaxMin.dll');// Using the FullName of a type that has not been registered yet (autodiscoverable)
$datetime2 = $runtime->TypeFromAssembly("System.DateTime", "mscorlib, ....");$datetime->Instantiate();
echo $datetime->ToShortDateString()->Val(); // Outputs 01/01/0001
// We can only use Int32 from native PHP, so parse
// an Int64 that is equivalent to (long) in the DateTime constructor.
$ticks = $runtime->TypeFromName("System.Int64")->Parse('98566569856565656');$datetime->Instantiate($ticks);
echo $datetime->ToShortDateString()->Val(); // Outputs 07/05/0313
// We can only use Int32 from native PHP, so parse
// an Int64 that is equivalent to (long) in the DateTime constructor.
$ticks = $runtime->TypeFromName("System.Int64")->Parse('98566569856565656');// Dump a .Net System.Timers.Timer object!
$data = $timer->GetPhpFromJson();var_dump($data);// Outputs:
// object(stdClass)[38]
// public 'AutoReset' => boolean true
// public 'Enabled' => boolean false
// public 'Interval' => int 100
// public 'Site' => null
// public 'SynchronizingObject' => null
// public 'Container' => null
// Check if today is monday
$IsMonday = $runtime->TypeFromName("System.DateTime")->Now->DayOfWeek->
Equals($runtime->TypeFromName("System.DayOfWeek")->Enum('Monday'));?>
paul at completewebservices dot com dot au ¶
17 years ago
It seems that PHP does not support setting of properties with arguments. This is how I got around the problem:
// Object with method I want to set
$auth = new COM("AUTHXOCX.AuthXOCXCtrl.1");
// ScriptControl
$oScript = new COM("MSScriptControl.ScriptControl");
$oScript->Language = "VBScript";
$oScript->AllowUI = TRUE;
// Add out object to the control
$oScript->AddObject('auth', $auth, true);
// Create a VBScript function that allow me to set them
$oScript->AddCode(
'Function fixAccess(accessname)
auth.AuthDataReferrerEnabled(accessname) = 1
auth.AuthDataAuthentiXDBEnabled(accessname) = 0
End Function');
// Execute function
$oScript->Run("fixAccess", $dir);
csaba at alum dot mit dot edu ¶
18 years ago
Basic Windows IO
If you are using a command line (CLI) version of PHP on a Win32 platform, you might like to have a simple graphical interface to get input and output. The following gives an illustration for both.
<?php
// first we get some input
$oScript = new COM("MSScriptControl.ScriptControl");
$oScript->Language = "VBScript";
$title = "I/O Demo: Input half";
$initial = "Change this value";
$prompt = "Please enter a value";$code = <<<EOF
Function getInput()
inVal = InputBox("$prompt", "$title", "$initial")
getInput = inVal 'how return values are assigned in VB
End Function
EOF;$oScript->AddCode($code);
$input = $oScript->Eval("getInput()");
if (gettype($input)=="NULL") $input = "Input box was cancelled";// now we show some output
$oWSH = new COM("WScript.Shell");
$title = "I/O Demo: Output half";
$timeout = 2; // 0 for no timeout
$style = 0 + 48; // buttons to show + alert symbol
$oWSH->Popup($input, $timeout, $title, $style);
?>
This is example is overblown for illustrative purposes. The whole input part collapses if the amount of code used is a single statement. So, this would have sufficed:
$code = "InputBox("$prompt", "$title", "$initial")";
$input = $oScript->Eval($code);
This technique exposes quite a bit of scripting power tied into the Windows operating system and VBScript. However, you should have a really specific reason for using it since VBScript tends to be dog slow when compared to PHP in my tests. Simple IO like this is good, however, and the popup size can be quite large. Furthermore, this could be a viable route to go for accessing WinAPI's.
http://www.ss64.com/wsh/popup.html shows some documentation for $oWSH->Popup
Csaba Gabor from Vienna
pavanphp Gudipati ¶
13 years ago
Here I Am describing a Program from which you can connect any exe(software) using COM component.
pdf2swf is a software for converting PDf to SWF files.
This is best example for WScript.Shell
<?php
#code for pdf to swf using pdf2swf software
#this codeworks in windows environment.
## Important Parameters
$software_path ="C:\SWFTools\pdf2swf" ;
$pdf_path ="C:\SWFTools\abcd.pdf" ;
$argument = "-o";
$swf_output ="C:\SWFTools\abcd.swf" ;
#actual code
$cmd =" $software_path $pdf_path $argument $swf_output";
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C $cmd ", 0, true);
# 0 for command prompt invisible mode
# 3 for visible
?>
Eion Robb ¶
9 years ago
All the examples here show
<?php $var = new COM('Whatever.Something') or die(); ?>
however the 'or die()' bit is redundant as a fatal 'com_exception' exception will be thrown, killing your script. If you want to handle the 'or' condition you need to catch the exception, eg
<?php
try {
$var = new COM('Whatever.Something');
} catch (com_exception $e) {
//couldn't load, do something else like register
exec('regsvr32 /s whatever.dll');
}
?>
hwcshirley at gmail dot com ¶
16 years ago
Reply to: djogopatrao at gmail dot com
I have change little bit your code. Its work.
$COM_Object = "CrystalDesignRunTime.Application";
$my_report = "C:\appserv\www\mc\test.rpt";
$my_pdf = "C:\appservwww\mc\test.pdf";
$crapp= New COM($COM_Object) or die("Unable to Create Object");
$creport = $crapp->OpenReport($my_report, 1);
$creport->ReadRecords(); // attention!
$creport->ExportOptions->DiskFileName=$my_pdf;
$creport->ExportOptions->PDFExportAllPages=true;
$creport->ExportOptions->DestinationType=1; // Export to File
$creport->ExportOptions->FormatType=31; // Type: PDF
$creport->Export(false);
volker at kybs dot de ¶
17 years ago
To use the windows speech system install the "Speech SDK 5.1 for Windows? applications" from the Microsoft homepage (free download).
Then you can do Text2Speech like this:
$voice = new COM("SAPI.SpVoice");
$voice->Speak("Hello, lets have a conversation");
deletethis@bjoern(at)syltonline(doot)de ¶
17 years ago
If you can get the old "TaskScheduler.dll" and register it, it is quite simple to handle and start Windows tasks from PHP.
This is extremely useful when you got things to do which must be run in a different user context. Prepare a task with the user and use PHP & Com to build a webinterface for it.
Here is an example how to start a Windows task:
function start_task ($taskname) {
$SchedObj = new COM("Scheduler.SchedulingAgent.1");
foreach ($SchedObj->Tasks as $task) {
if (strtolower( (string) $task) == strtolower( $taskname.".job" )) {
$task->Run();
}
}
}
I'm also working on a class to handle infos, adding and removing of tasks via PHP. If you are interested drop me a line.
phpguy _from_ toshpro _dot_ com ¶
18 years ago
if you ever need to connect to an access database (on the server, using the IIS or APACHE user account), here is code below that can help to ease the process. It is written like this because we have a MySQL as well as an Access database and need to be able to switch back and forth. The runQuery function will return a 2 dimensional array and can use column names or numbers for the index of the internal arrays. External arrays are always integer-indexed. We also have found issues with Access staying open and growing from 7MB to 2.5GB within two hours, so we open and close it each time we run a query. If it is not a select query it will return TRUE. if it fails with errors, it will return false.
There is a showErr flag that we mostly only use for MySQL, since you can output MySQL errors without PHP errors. Access doesn't appear to be that friendly.
<?phpfunction runQuery(&$conn,
$strSQL,
$associative=true,
$debug=false,
$showSQL=false,
$showErr=false) {
return runMSQuery($conn,$strSQL,$associative,$debug,$showSQL,$showErr);
//return runMyQuery($conn,$strSQL,$associative,$debug,$showSQL,$showErr);
}
function
openMSDB($dbfile="c:/path/and/filename.mdb") {
if (!@$conn = new COM("ADODB.Connection"))
exit("Unable to create an ADODB connection<br>");
$strConn = "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=".$dbfile;
@$conn->open($strConn);
if($conn->State == 0) return false;
return $conn;
}
function
closeMSDB(&$conn) {
@$conn->Close();
$conn = null;
return true;
}
function
runMSQuery(&$conn,
$strSQL,
$associative=true,
$debug=false,
$showSQL=false,
$showErr=false) {
if(!is_object($conn)) if(!$conn=openMSDB()) return false;
if( $showSQL || $debug ) echo $strSQL."n<br>n";
$rtn = false;
if($debug) $rs = $conn->execute($strSQL);
else $rs = @$conn->execute($strSQL);
if(!
$rs) {
if($showErr) echo "Error running SQL.<br>n";
closeMSDB($conn);
return false;
}
if ($rs->State == 0) {
closeMSDB($conn);
return true;
} else {
$rows=0;
while(!$rs->EOF) {
for($i=0;$i<$rs->Fields->count;$i++) {
$rsf = $rs->Fields($i);
if($associative) $rtn[$rows][$rsf->Name] = $rsf->value;
else $rtn[$rows][$i] = $rsf->value;
}
$rows++;
$rs->MoveNext();
}
if($rs->State != 0) $rs->Close();
$rs = null;
}
if($debug) var_dump($rtn);
closeMSDB($conn);
return $rtn;
}
?>
admin at CdnDomainRegistry dot ca ¶
19 years ago
Here's an example/comparison of VBScript vs PHP's COM component to connect to an IIS Webserver.
The examples backup the IIS MetaBase (your server's website(s) configuration) in IIS.
VBScript:
Dim obj, iFlags
Set obj = GetObject("IIS://LocalHost")
' Backup to next available version number.
' Set flags to save the metabase first and
' force the backup even if save fails.
iFlags = (MD_BACKUP_SAVE_FIRST or MD_BACKUP_FORCE_BACKUP)
obj.Backup "MyBackups", MD_BACKUP_NEXT_VERSION, iFlags
' Note: The above was copied from Microbucks.
' This is how you would have to do it.
' i.e. use the actual constant's value.
iFlags = (2 or 4)
obj.Backup "MyBackups", &HFFFFFFFF, iFlags
PHP:
<?php
$obj = new COM("IIS://LocalHost")or die("Unable to instantiate IIS");
$err = com_invoke ($obj, "Backup", "MyBackups", "&HFFFFFFFF", 2|4 );
$obj = null;
print "err=".$err; // returns 0 on success
?>
Glen
djogopatrao at gmail dot com ¶
17 years ago
My previous notes about Crystal Reports have vanished away, but anyway I was up to correct them.
The code above connects to COM (beware of the version number! search on the Registry Editor for the correct name to put in $COM_Object), opens a report from a file ($my_report), reload records from database and then exports it to a PDF file ($my_pdf).
* IMPORTANT * this code, when invoked from the browser (I'm using Apache) AND with a report that connects to a ODBC database, does not work (the problem is specificly the line ReadRecords(), but may arise without it too when data is not saved into the report file).
But when you run this code by PHP-CLI (that is, by the command line), it works ok! I bug-reported this, but have no answer till now ( give it a vote, if you thing it's important: http://bugs.php.net/bug.php?id=36959 ).
Also, exceptions may arise, so put it on a try_catch block.
--------------------------
// by dfcp '06 (djogopatrao@gmail.com)
$COM_Object = "CrystalReports11.ObjectFactory.1";
$my_report = "C:\report.rpt";
$my_pdf = "C:\report.pdf";
$ObjectFactory= New COM($COM_Object);
$crapp = $ObjectFactory->CreateObject("CrystalDesignRunTime.Application");
$creport = $crapp->OpenReport($my_report, 1);
$creport->ReadRecords(); // attention!
$creport->ExportOptions->DiskFileName=$my_pdf;
$creport->ExportOptions->PDFExportAllPages=true;
$creport->ExportOptions->DestinationType=1; // Export to File
$creport->ExportOptions->FormatType=31; // Type: PDF
$creport->Export(false);
johnno1985 at gmail dot com ¶
14 years ago
I couldn't find an example anywhere on what I was trying to do. After taking bits from everyone I ended up with something that worked.
Im using Crystal Reports 10, MS SQL Server 2005, Apache2, and PHP5.
My report uses an SQL Store Procedure and passes some parameter to get the report data, so our PHP must set the database logon info and pass the parameters.
Below is the solution I came up with:
<?php//------ Variables ------
$my_report = "C:\Apache2htdocs\test\MyReport.rpt"; //This must be the full path to the file
$my_pdf = "C:\Apache2htdocs\test\MyReport.pdf";//------ Create a new COM Object of Crytal Reports 10 ------
$ObjectFactory= new COM("CrystalReports10.ObjectFactory.1");//------ Create a instance of library Application -------
$crapp = $ObjectFactory->CreateObject("CrystalDesignRunTime.Application.10");//------ Open your rpt file ------
$creport = $crapp->OpenReport($my_report, 1);//------ Set database logon info ------
$creport->Database->Tables(1)->SetLogOnInfo("MYSERVER", "Database", "user", "password");//------ Suppress the Parameter field prompt or else report will hang ------
$creport->EnableParameterPrompting = 0;//------ DiscardSavedData make a Refresh in your data -------
$creport->DiscardSavedData;
$creport->ReadRecords();//------ Pass formula fields --------
$creport->FormulaFields->Item(1)->Text = ("'My Report Title'");
$creport->ParameterFields(1)->AddCurrentValue ("FirstParameter");
$creport->ParameterFields(2)->AddCurrentValue (2000);//------ Export to PDF -------
$creport->ExportOptions->DiskFileName=$my_pdf;
$creport->ExportOptions->FormatType=31;
$creport->ExportOptions->DestinationType=1;
$creport->Export(false);//------ Release the variables ------
$creport = null;
$crapp = null;
$ObjectFactory = null;//------ Embed the report in the webpage ------
print "<embed src="MyReport.pdf" width="100%" height="100%">"?>
Obviously there's plenty of room for refinement in there, but it works and should get you going on the right track. I also found that during testing, the script was hanging before supressing the parameter prompt. Once this had happened, some exports that had been working earlier stopped completely and wouldn't work again untill I restarted Apache. This might be something worth doing if you keep having issues after a script has hung.
casanoteva at yahoo dot com ¶
14 years ago
Hi, just wanna share to everyone after tried and tested many many times to create report in PDF format from Crystal Report.
Finally I can do it without any error.
Here is my system; PHP 5.1.6, MSSQL2005 and Crystal Report Server XI RL2
<?php//- Variables - for your RPT and PDF
echo "Print Report Test";
$my_report = "D:\Folder1\SubFolder1\Report.rpt"; //
rpt source file
$my_pdf = "D:\Folder1\SubFolder1\Report.pdf"; // RPT export to pdf file
//-Create new COM object-depends on your Crystal Report version
$ObjectFactory= new COM("CrystalReports115.ObjectFactory.1") or die ("Error on load"); // call COM port
$crapp = $ObjectFactory-> CreateObject("CrystalDesignRunTime.Application"); // create an instance for Crystal
$creport = $crapp->OpenReport($my_report, 1); // call rpt report
// to refresh data before
//- Set database logon info - must have
$creport->Database->Tables(1)->SetLogOnInfo("servername", "DBname", "user", "password");//- field prompt or else report will hang - to get through
$creport->EnableParameterPrompting = 0;//- DiscardSavedData - to refresh then read records
$creport->DiscardSavedData;
$creport->ReadRecords();//export to PDF process
$creport->ExportOptions->DiskFileName=$my_pdf; //export to pdf
$creport->ExportOptions->PDFExportAllPages=true;
$creport->ExportOptions->DestinationType=1; // export to file
$creport->ExportOptions->FormatType=31; // PDF type
$creport->Export(false);//------ Release the variables ------
$creport = null;
$crapp = null;
$ObjectFactory = null;//------ Embed the report in the webpage ------
print "<embed src="D:\Folder1\SubFolder1\Report.pdf" width="100%" height="100%">"?>
That's all for me, anyway in Crystal Report I have to check DisCardSavedData, Save data with report and AutoSave (1 min) in Option Tab and also in Report Options I have to check save dat with report, too.
Hope this should be help to anybody
Casanoteva
IceNV ¶
6 years ago
Under PHP7(.x) 64-bit, there is a documentation error (on the MSDN side, already reported) causing problems if you try to manipulate the registry (StdRegProv) through COM.
Indeed, MSDN reports that the registry hive/root must be reported as a uint32, which means that, in PHP 64-bit, one would create a new Variant($root,VT_UI4).
This would throw a type mismatch error.
It seems internally and contrary to what MSDN says (as of now, 2016-12-13), the root parameter must actually be passed as an int32.
So you have to :
1. convert your root number to a negative int to avoid out-of-bounds exceptions when creating the variant below (basically the integer being >= 2^31, you have to wrap it around the 32-bit signed integer maximum yourself) ;
2. create a new Variant($root,VT_I4) ;
3. pass that Variant to your COM API call.
This did not happen before PHP 7.0 as it always used VT_I4 internally before (see https://bugs.php.net/bug.php?id=73605), but now it respects (and is right to do so) the real integer length, so without an explicit conversion, PHP7 would now output a VT_I8 on x64, versus VT_I4 for PHP5.
Danil_lll ¶
4 years ago
Hello. There is a code c# which works with the COM-library (written on C++) - hybrid.dll.
I need to rewrite this code from C# to PHP (that is connect the COM-library to the PHP code)
Here is the function that is called inside C#:
Gr = HybridLib.Hybrid_Color.GREEN;
And here is the list of "Hybrid_Color"which in the library:
enum Hybrid_Color
{
GREEN = 0x00000001,
BLACK = 0x00000002,
BLUE = 0x00000004,
};
The library itself was launched in PHP:
$PHP_Lib = new COM("HybridLib.Hybrid");
Can not assign value:
$gr = $PHP_Lib -> ..... ?????
What I write next, I don't know...
Tell me, please, who knows...
dpchiesa at hotmail dot com ¶
14 years ago
Here's a way to create an AES-Encrypted ZIP file in PHP, using DotNetZip via the COM class.
<?php
try
{
$fname = "zip-generated-from-php-" . date('Y-m-d-His') . ".zip";
$zipOutput = "c:\temp\" . $fname;
$zip = new COM("Ionic.Zip.ZipFile");
$zip->Name = $zipOutput;
$dirToZip= "c:\temp\psh";
#$dirToZip= "c:\dinoch\webs\php";
$zip->Encryption = 3;
$zip->Password = "AES-Encryption-Is-Secure";
$zip->AddDirectory($dirToZip);
$zip->Save();
$zip->Dispose();
if (
file_exists($zipOutput))
{
header('Cache-Control: no-cache, must-revalidate');
header('Content-Type: application/x-zip');
header('Content-Disposition: attachment; filename=' . $fname);
header('Content-Length: ' . filesize($zipOutput));
readfile($zipOutput);
unlink($zipOutput);
}
else
{
echo '<html>';
echo ' <head>';
echo ' <title>Calling DotNetZip from PHP through COM</title>';
echo ' <link rel="stylesheet" href="basic.css"/>';
echo ' </head>';
echo '<body>';
echo '<h2>Whoops!</h2>' . "<br/>n";
echo '<p>The file was not successfully generated.</p>';
echo '</body>';
echo '</html>';
}
}
catch (Exception $e)
{
echo '<html>';
echo ' <head>';
echo ' <title>Calling DotNetZip from PHP through COM</title>';
echo ' <link rel="stylesheet" href="basic.css"/>';
echo ' </head>';
echo '<body>';
echo '<h2>Whoops!</h2>' . "<br/>n";
echo '<p>The file was not successfully generated.</p>';
echo '<p>Caught exception: ', $e->getMessage(), '</p>', "n";
echo '<pre>';
echo $e->getTraceAsString(), "n";
echo '</pre>';
echo '</body>';
echo '</html>';
}?>
drich at nea-online dot net ¶
19 years ago
Changing Fields (Not FORMFIELDS) In MS Word....
Searched most of the afternoon for this, as it is something needed here for our projects. Maybe this can help someone else as well.
<?php
$input = "x:\path\to\test.doc";
$word = @new COM("word.application") or die("Unable to instantiate Word");
print "Loaded Word, version {$word->Version}n<br>";
$word->Visible = 1;
print "Set Word To Visible<br>";
$word->Documents->Open($input);
print "Opened $input<br>";
$word->Activate;
print "Activated Word<br>";
"Editing Fields<br>";
$Field = $word->ActiveDocument->Fields(1);
$fieldData = $Field->Result;
$fieldData->Text = "Testing";
// Print the document.
$word->Printout();
// closing word
$word->Quit();
// free the object
$word->Release();
$word = null;
?>
sodeh at dana dot ir ¶
18 years ago
for connection to sql server and know the utf-8 code
use this method to connect the database
$db = new COM("ADODB.Connection",NULL, 65001 );
sadi at unicornsoftbd dot com ¶
17 years ago
If your PHP is configured as ISAPI module then the COM object you create in ur php page will use the same memory space as the web server. If your COM object takes too much memory then this may force some COM error as PHP does not handle the memory usage of COM itself. And also Web server doesnt know what do to free COM memory. But if you use PHP as CGI then PHP itself handle the memory usage of COM. Once in a script where i used COM and PHP as ISAPI module i got unknown COM error.But when i used PHP as CGI there was no error. So those who want to use COM object in your application please be careful about it.
jon at jonroig dot com ¶
19 years ago
Here's a simple example of printing formatted templates with MS Word.
To begin with, I've already built my invoice in Word and saved it as an RTF file.
In this case, I'm using "sampleInvoice.php"
Fields that will be replaced have been marked as ||CUSTOMER ID|| and so on.
<?php
$dataText = "";
// open the file
if(!($fp= fopen ("C:wordTestsampleInvoice.rtf", "r"))) die ("Can't open");
$dataText = fread($fp, 2000000);
fclose ($fp);
// replace the template fields
$dataText = str_replace ("||CUSTOMER ID||",$customerIDValue, $dataText);
// save the file as an rtf
$timeStamp = time();
$saveFile = "C:/wordTemp/".$timeStamp."-invoice-".$customerIDValue.".rtf";
if(!($fq= fopen ($saveFile, "w+"))) die ("Can't open");
fwrite ($fq, $dataText);
fclose ($fq);
// initialize the word components
$word = new COM("word.application") or die("Unable to instantiate Word");
"Loaded word version ($word->Version)n";
$word->visible = true;
$word->Documents->Add();
// open the file
$word->Documents->Open("$saveFile");
// output the file to the default printer
$word->ActiveDocument->PrintOut(1);
// shutdown word
$word->Quit();
?>
info at sharedprojects dot nl ¶
19 years ago
<?php
// An other way to connect
// this connect to a ODBC-datasourced added in the
// ODBC Data Source Administrator under System DSN
$odbcname = "northwind";
$conn->Open("DSN=$odbcname");
?>
mstaiger at informatik dot uni-siegen dot de ¶
21 years ago
The example above (MS Word) is quiet unsatisfying, as I find. Especially since I couldn't find anybody who actually got it running, including myself.
You might therefore give this Powerpoint-example a chance:
----------------------------------
#Instantiate a PowerPoint object.
$ppoint = new COM("PowerPoint.application") or die("Unable to instantiate PowerPoint");
<?php
#Create a new presentation
$ppoint->Presentations->Add() or die ("Could not create presentation");
//Add a slide
$slide=$ppoint->Presentations[1]->Slides->Add(1,1);
//Get the name of this slide
$slidename = $slide->Name();
echo"<br>slidename : $slidename";
//Change the name of this new slide
$slide->Name = "New Slidename";
$slidename = $slide->Name();
echo"<br>NEW slidename : $slidename";
//Save the presentation
$ppoint->Presentations[1]->SaveAs("c:/InetPub/www/test.ppt");
$ppoint->Presentations[1]->Close();
//closing powerpoint
$ppoint->Quit();
//free the object
$ppoint->Release();
$ppoint = null;
-----------------------
?>
the config is :
w2k-server/office2k/iis5/php 4.1.1 isapi
If you happen to run into a working word2k-example, please let me know.
Marc
eguan1ao_at_yahoo_dot_com ¶
20 years ago
An example of connecting to a Microsoft Access database, executing an SQL query, and displaying the results in HTML.
<?php
$db
= 'C:\Program Files\Microsoft Office\Office\Samples\Northwind.mdb';$conn = new COM('ADODB.Connection');// Two ways to connect. Choose one.
$conn->Open("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=$db");
//$conn->Open("DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=$db");$sql = 'SELECT ProductName, QuantityPerUnit, UnitPrice
FROM Products
ORDER BY ProductName';
$rs = $conn->Execute($sql);?>
<table>
<tr>
<th>Product Name</th>
<th>Quantity Per Unit</th>
<th>Unit Price</th>
</tr>
<?php while (!$rs->EOF): ?>
<tr>
<td><?= $rs->Fields['ProductName']->Value ?></td>
<td><?= $rs->Fields['QuantityPerUnit']->Value ?></td>
<td><?= $rs->Fields['UnitPrice']->Value ?></td>
</tr>
<?php $rs->MoveNext() ?>
<?php endwhile ?>
</table>
<?php
$rs
->Close();
$conn->Close();?>
amenthes at NOSPAM dot gmail dot com ¶
15 years ago
If you ever want to save an Excel(.xls) file into the Office Excel 2003 XML format, just use
$excel->Workbooks[1]->SaveAs('new_file_name.xml', 46);
That's the number you need, 46.
Время на прочтение
2 мин
Количество просмотров 8.4K
Доброго времени суток Хабр!
Хочу немного рассказать о том, что нужно сделать при использования COM объекта для конвертирования файлов word в какой-нибудь из доступных форматов. Сам столкнулся со множеством проблем, облазил много форумов, русских мануалов практически нет. Решил написать, больше для себя, но вдруг кому-то еще пригодится?
Если у вас IIS, есть вероятность того, что вам это не понадобится, статья исключительно о настройке работы под Apache
Первая и основная проблема, которая вызывала разрыв мозга:
Call to undefined method variant::SaveAs()
при попытке сохранить документ.
Эксперименты часто приводили к ошибкам отсутствия свойства в объекте или ошибкам разбора документа, самая частая —
Fatal error: Uncaught exception 'com_exception'
with message 'Source: Unknown<br/>Description: Unknown'
Что нужно сделать для того, чтобы процесс все-таки заработал:
- Нужен виндовый сервер
- Установленный на нем пакет MsOffice (или не целый пакет, а только необходимые его части)
- Настройка разрешения для COM
- Далее идем в Start->Administrative Tools->Component Services
- Разворачиваем меню Component Services->Computers->My Computer->DCOM Config
- В списке ищем необходимые нам приложения COM (как правило все они начинаются со слов Microsoft… )
- Жмем правой кнопкой по интересующему выбираем Properties переходим на закладку Securety в секции Access Permissions ставим точку в Customize и жмем на кнопку Edit
- В появившемся окне жмем add и добавляем пользователя который имеет права на запуск apache (вводим в нижнее поле логин пользователя и жмем Check Names если все нормально, то винда его найдет, жмем ок)
- В списке выбираем добавленного пользователя и ставим ему галочку Remote Acceess
- Подтверждаем все изменения (жмем ok 2 раза)
- Повторить пункты 4-7 для нужных приложений
- Настройка apache
- После того, как права на COM розданы все в том же окне (Component Services) идем в раздел Services находим там apache жмем по нему правой кнопкой выбираем Properties переходим на вкладку Log On жмем на кнопку Browse вводим того же пользователя, которого указывали выше для COM объектов, жмем ок, вводим пароль для пользовательской учетки, чтобы apache мог запуститься жмем ок
- Перезапускаем apache, если все правильно, то перезапуск пройдет удачно
- Настройка php.ini
- Открываем php.ini
- Ищем секцию [COM]
- Раскоменчиваем строки:
com.allow_dcom = true com.autoregister_typelib = true com.autoregister_casesensitive = false com.autoregister_verbose = true
Ищем секцию Dynamic Extensions
Добавляем строку:extension=php com_dotnet.dll
Перезапускаем apache
- Важно: после выставления прав на COM объекты нужно запустить word (по-крайней мере у меня без этого разрешения не сменились)
- Profit
PS не забываем убивать процесс ворда в случае неудачных экспериментов, так как он запускается, открывает файл на чтение и висит.
Далее можно использовать стандартные функции COM для работы с объектом, напрмер, так:
com_load_typelib('Word.Application'); $name = 'test.docx'; $path = dirname(__FILE__) . "\tmp\". $name ; $word = new COM("word.application") or die ("Невозможно создать COM объект"); $word->Documents->Open( $path ); mkdir(dirname(__FILE__) . "\tmp\{$name}"); $folder = dirname(__FILE__) . "\tmp\{$name}"; $word->ActiveDocument->SaveAs( $folder . "\{$name}.html", 8); $word->Quit(); $word = null;
Мы живем в мире, где PHP разработчикам приходится время от времени взаимодействовать с операционной системой Windows. WMI (Windows Management Interface, Интерфейс управления Windows) — один из таких примеров — взаимодействие с Microsoft Office.
В данной статье мы рассмотрим простую интеграцию между Word и PHP: генерацию документа Microsoft Word на основе полей ввода в HTML-форме с помощью PHP (и его расширения Interop).
Подготовительные шаги
Первым делом убедимся, что у нас настроено базовое окружение WAMP. Так как Interop присутствует только в Windows, то нам необходимо, чтобы наш сервер Apache и инсталляция PHP были развернуты на Windows машине. В этом качестве я использую EasyPHP 14.1, который крайне прост в установке и настройке.
Следующим делом необходимо установить Microsoft Office. Версия не очень важна. Я использую Microsoft Office 2013 Pro, но любая версия Office старше 2007 должна подойти.
Также необходимо убедиться, что у нас установлены библиотеки для разработки приложения Interop (PIA, Primary Interop Assemblies, Основные Сборки Interop). Узнать это можно открыв Проводник Windows, и перейдя в директорию <Windows Directory>assembly
, и там мы должны увидеть набор установленных сборок:
Здесь можно увидеть элемент Microsoft.Office.Interop.Word
(подчеркнут на скриншоте). Это будет та сборка, которую мы будем использовать в нашей демонстрации. Пожалуйста, обратите особое внимание на поля “Assembly name (Имя сборки)”, “Version (Версия)” и “Public key token (Токен публичного ключа)”. Их мы скоро будем использовать в нашем PHP скрипте.
В этой директории также присутствуют и другие сборки (включая и все семейство Office), доступные для использования в своих программах (не только для PHP, но также и для VB.net, C#, и т.д.).
Если список сборок не включает весь пакет Microsoft.Office.Interop
, то нам нужно либо переустановить Office, добавив PIA, или вручную загрузить пакет с сайта Microsoft и установить его. Для более детальных инструкций обратитесь к этой странице на MSDN.
Замечание: к загрузке и установке доступен только дистрибутив PIA Microsoft Office 2010. Версия сборок в этом пакете 14.0.0, а 15 версия поставляется только с Office 2013.
И, наконец, необходимо включить расширение php_com_dotnet.dll
в php.ini
и перезапустить сервер.
Теперь можно перейти к программированию.
HTML форма
Так как основная часть данного примера ложится на серверную сторону, мы создадим простую страничку с формой, которая будет выглядеть следующим образом:
У нас есть текстовое поле для имени, группа переключателей для пола, слайдер для возраста, и область ввода текста для ввода сообщения, а также небезызвестная кнопка “Отправить”.
Сохраните этот файл как “index.html” в директории виртуального хоста, чтобы до него можно было добраться по адресу типа http://test/test/interop
.
Серверная часть
Файл-обработчик на серверной стороне — это основная цель нашего разговора. Для начала я приведу полный код этого файла, а потом объясню его шаг за шагом.
<?php $inputs = $_POST; $inputs['printdate']=''; // Инициализация значения, чтобы избежать замечания от PHP о том, что в POST данных нет переменной “printdate” $assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'; $class = 'Microsoft.Office.Interop.Word.ApplicationClass'; $w = new DOTNET($assembly, $class); $w->visible = true; $fn = __DIR__ . '\template.docx'; $d = $w->Documents->Open($fn); echo "Документ открыт.<br><hr>"; $flds = $d->Fields; $count = $flds->Count; echo "В документе $count полей.<br>"; echo "<ul>"; $mapping = setupfields(); foreach ($flds as $index => $f) { $f->Select(); $key = $mapping[$index]; $value = $inputs[$key]; if ($key == 'gender') { if ($value == 'm') $value = 'Mr.'; else $value = 'Ms.'; } if($key=='printdate') $value= date ('Y-m-d H:i:s'); $w->Selection->TypeText($value); echo "<li>Назначаю полю $index: $key значение $value</li>"; } echo "</ul>"; echo "Обработка завершена!<br><hr>"; echo "Печатаю, пожалуйста, подождите...<br>"; $d->PrintOut(); sleep(3); echo "Готово!"; $w->Quit(false); $w=null; function setupfields() { $mapping = array(); $mapping[0] = 'gender'; $mapping[1] = 'name'; $mapping[2] = 'age'; $mapping[3] = 'msg'; $mapping[4] = 'printdate'; return $mapping; }
После того, как мы записали в переменную $inputs
значения, полученные из формы, а также создали пустой элемент с ключом printdate
(зачем мы это сделали — обсудим позже), мы переходим к четырем очень важным строкам:
$assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'; $class = 'Microsoft.Office.Interop.Word.ApplicationClass'; $w = new DOTNET($assembly, $class); $w->visible = true;
Манипулятор COM в PHP требует создания экземпляра класса в рамках “сборки”. В нашем случае мы работаем с Word. Если взглянуть на первый скриншот, то можно записать полную сигнатуру сборки для Word:
- “Name”, “Version”, “Public Key Token” — все это берется из информации, которую можно просмотреть в
“c:Windowsassembly“
. - “Culture” всегда
neutrual
Класс, на который мы хотим ссылаться, всегда называется “имя.сборки”+ “.ApplicationClass
“.
Установив два этих параметра мы сможем получить объект для работы с Word.
Этот объект может оставаться в фоне, или мы можем перевести его в рабочий режим установкой атрибута visible
в true
.
Следующим шагом открываем документ, требующий обработки, и записываем экземпляр “документа” в переменную $d
.
Чтобы создать в документе содержимое, основанное на данных с формы, можно пойти несколькими путями.
Самым неправильным было бы жестко прописать содержимое документа в PHP, а потом вывести его в документ Word. Я настоятельно рекомендую этого не делать по следующим причинам:
- Вы теряете гибкость. Любые изменения в выходном файле потребуют изменения кода PHP.
- Это нарушает разделение управления и вида
- Применение стилей к содержимому документа (выравнивание, шрифты, стили, и т.д.) в скрипте сильно увеличит количество строк кода. Программное изменение стилей чересчур громоздко.
Другим вариантом будет использование поиска и замены. У PHP есть хорошие встроенные средства для этого. Мы можем создать документ Word, в котором разместим метки со специальными разделителями, которые в последствии будут заменены. Например, мы можем создать документ, который будет содержать следующий фрагмент:
а с помощью PHP мы легко можем заменить его на содержимое поля “Имя”, полученное с формы.
Это просто, и избавляет нас ото всех неприятных последствий, с которыми мы сталкиваемся в первом способе. Нам всего лишь нужно определиться с правильным разделителем, и в этом случае мы, получается, используем шаблон.
Я рекомендую третий способ, и он основывается на более глубоком знании Word. В качестве заполнителей мы будем использовать поля, а с помощью PHP кода будем непосредственно обновлять значения в полях соответствующими значениями.
Этот подход гибкий, быстрый, и согласуется с лучшими практиками работы с Word. С его помощью также можно избежать полнотекстового поиска в документе, что хорошо сказывается на производительности. Замечу, что у этого решения также есть недостатки.
Word с самого начала не поддерживал именные индексы для полей. Даже если мы и указали имена для создаваемых полей — нам все равно необходимо пользоваться числовыми идентификаторами этих полей. Это также объясняет, зачем нам нужно использовать отдельную функцию (setupfields
) для того, чтобы задать соответствие индекса поля и имени поля из формы.
В этом демонстрационном уроке мы будем использовать документ с 5 полями MERGEFIELD
. Шаблонный документ разместим там же, где и наш скрипт-обработчик.
Прошу заметить, что поле printdate
не имеет соответствующего поля на форме. Вот зачем мы добавили пустой элемент printdate
в массив $inputs
. Без этого скрипт все же будет запускаться и работать, но PHP будет выдавать предупреждение, что индекс printdate
отсутствует в массиве $inputs
.
После замены полей новыми значениями мы отпечатаем документ с помощью
Метод PrintOut
принимает несколько необязательных параметров, и мы используем самую простую его форму. Так будет отпечатана одна копия документа на принтере по умолчанию, который присоединен к Windows-машине.
Также можно вызвать PrintPreview
, чтобы взглянуть на получившийся результат, прежде чем его отпечатать. В полностью автоматическом окружении мы, конечно же, будем использовать метод PrintOut
.
Необходимо подождать некоторое время, прежде чем завершить работу с приложением Word, так нужно время на то чтобы поставить в очередь задание на печать. Без delay(3)
метод $w->Quit
выполняется незамедлительно, и задание не ставится в очередь.
Наконец, мы вызываем $w->Quit(false)
, что закрывает приложение Word, которое было вызвано нашим скриптом. Единственным параметром, передаваемым в метод, является указание сохранить файл перед выходом. Мы сделали правки в документе, но мы не хотим их сохранять, так как нам нужен чистый шаблон для последующей работы.
После того, как мы закончили с кодом, можем загрузить нашу страницу с формой, забить некоторые значения, и отправить её. Нижеприведенные изображения показывают результат работы скрипта, а также обновленный документ Word:
Улучшение скорости обработки и немного подробнее о PIA
PHP — слабо типизированный язык. Объект COM типа Object
. Во время написания скрипта у нас нет возможности получить описание объекта, будь оно приложением Word, документом или полем. Мы не знаем, какие свойства есть у этого объекта, или какие он поддерживает методы.
Это сильно замедлит скорость разработки. Чтобы ускорить разработку, я бы рекомендовал писать функции сначала на C#, а после переводить код в PHP. Я могу рекомендовать бесплатную IDE для разработки на C# под названием “#develop”. Найти ее можно здесь. Я предпочитаю ее Visual Studio, так как #develop меньше, проще и быстрее.
Миграция C# кода в PHP не так страшна, как кажется. Давайте я покажу вам пару строк на C#:
Word.Application w=new Word.Application(); w.Visible=true; String path=Application.StartupPath+"\template.docx"; Word.Document d=w.Documents.Open(path) as Word.Document; Word.Fields flds=d.Fields; int len=flds.Count; foreach (Word.Field f in flds) { f.Select(); int i=f.Index; w.Selection.TypeText("..."); }
Можно заметить, что код на C# очень похож на код PHP, который я показывал ранее. C# — строго типизированный язык, так что в этом примере можно заметить несколько операторов приведения типов, а также переменным необходимо указывать тип.
С указанием типа переменной, можно наслаждаться более понятным кодом и автодополнением, и скорость разработки существенно повышается.
Другой способ повысить скорость разработки на PHP — вызывать макрос в Word. Мы проводим ту же последовательность действий, а после сохраняем ее как макрос. Макрос написан на Visual Basic, который также просто перевести в PHP.
И, что самое важное — документация по Office PIA от Microsoft, особенно документация по пространствам имен каждого приложения Office является самым детальным справочным материалом. Наиболее используемые три приложения:
- Excel 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel(v=office.15).aspx
- Word 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.word(v=office.15).aspx
- PowerPoint 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint(v=office.15).aspx
Заключение
В этой статье мы показали, как заполнить данными документ Word с помощью библиотек PHP COM и возможностями взаимодействия Microsoft Office.
Windows и Office широко используются в повседневной жизни. Знать силу Office/Window и PHP будет полезно каждому PHP и Windows разработчику.
С помощью расширения PHP COM вам открывается дверь к использованию этой комбинации.
As I had pointed out in my previous article, PHP and WMI – Dig deep into Windows with PHP, we do live in a world where we PHP devs have to deal with the Windows operating system from time to time. WMI (Windows Management Interface) is one such occasion and Microsoft Office Interop is another – an even more important and more frequently used one.
In this article, we will see a simple integration between Word and PHP: to generate a Microsoft Word document based on the inputs in an HTML form using PHP (and its Interop extension).
Preparations
First, please make sure a typical WAMP environment has been set up in your Windows development machine. As Interop is purely a Windows feature, we will have to host Apache and PHP under Windows. In this instance, I am using EasyPHP 14.1, which is quite easy to install and configure.
Next, we will have to install Microsoft Office. Its version is not that critical. I am using Office 2013 Pro but any Office version later than 2007 should work.
We then have to make sure the libraries to develop an Interop application (called PIA, Primary Interop Assemblies) are installed. To ascertain this, we can open the Windows Explorer and navigate to: <Windows Directory>assembly
and we will see a bunch of installed PIAs:
We see a Microsoft.Office.Interop.Word
entry (underlined in the snapshot). This will be the PIA we use in this demo. Please pay special attention to its “Assembly Name”, “Version” and “Public Key Token”. These are to be used in our PHP scripts very soon.
In this directory, we can also see other PIAs (including the whole Office family) available for programming (not only for PHP, but also for VB.net, C#, etc)
If the PIAs list does not include the whole package of Microsoft.Office.Interop
, we will either re-install our Office and include PIA features; or we have to manually download the package from Microsoft and install it. Please consult this MSDN page for detailed instructions.
NOTE: Only Microsoft Office 2010 PIA Redistributable is available to download and install. The PIA version in this package is 14.0.0. Version 15 only comes with Office 2013 installation.
Finally, we have to enable the PHP extension php_com_dotnet.dll
in the php.ini
file and restart the server.
Now we can move on to the programming.
The HTML form
As the focus of this demo is on the back end processing, we will create a simple front end with a simple HTML form, which looks like the figure below:
We have a text field for “Name”, a radio button group for “Gender”, a range control for “Age” and a text area for “Message”; and finally, of course, a “Submit” button.
Save this file as “index.html” in an directory under the virtual host’s root directory so that we can access it with a URI like http://test/test/interop
.
The back end
The back end PHP file is the focus of our discussion. I will first list the code of this file, and then explain it step by step.
<?php
$inputs = $_POST;
$inputs['printdate']='';
// A dummy value to avoid a PHP notice as we don't have "printdate" in the POST variables.
$assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c';
$class = 'Microsoft.Office.Interop.Word.ApplicationClass';
$w = new DOTNET($assembly, $class);
$w->visible = true;
$fn = __DIR__ . '\template.docx';
$d = $w->Documents->Open($fn);
echo "Document opened.<br><hr>";
$flds = $d->Fields;
$count = $flds->Count;
echo "There are $count fields in this document.<br>";
echo "<ul>";
$mapping = setupfields();
foreach ($flds as $index => $f)
{
$f->Select();
$key = $mapping[$index];
$value = $inputs[$key];
if ($key == 'gender')
{
if ($value == 'm')
$value = 'Mr.';
else
$value = 'Ms.';
}
if($key=='printdate')
$value= date ('Y-m-d H:i:s');
$w->Selection->TypeText($value);
echo "<li>Mappig field $index: $key with value $value</li>";
}
echo "</ul>";
echo "Mapping done!<br><hr>";
echo "Printing. Please wait...<br>";
$d->PrintOut();
sleep(3);
echo "Done!";
$w->Quit(false);
$w=null;
function setupfields()
{
$mapping = array();
$mapping[0] = 'gender';
$mapping[1] = 'name';
$mapping[2] = 'age';
$mapping[3] = 'msg';
$mapping[4] = 'printdate';
return $mapping;
}
After setting up the $inputs
variable to hold the values posted from our form, and creating a dummy value for printdate
– we will discuss why we need this later – we come across these four critical lines:
$assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c';
$class = 'Microsoft.Office.Interop.Word.ApplicationClass';
$w = new DOTNET($assembly, $class);
$w->visible = true;
A COM manipulation in PHP requires an instantiation of a “class” within an “assembly“. In our case, we are to operate with Word. If we reflect on the first screenshot we showed, we will be able to construct the full signature of the Word PIA:
- “Name”, “Version”, “Public Key Token” are all taken from the information displayed when we browse to “
c:Windowsassembly
“. - “Culture” is always
neutrual
.
The class we are to invoke is always the assembly’s name plus “.ApplicationClass
“.
With these two parameters set, we will be able to instantiate a Word object.
This object can stay in the background or we can bring it to the foreground by setting its visible
attribute to true
.
Next, we open the document to be processed and assign the “document” instance to a $d
variable.
In that document, to create content based on the inputs from the HTML form, we have a few options.
The most unfavorable way is to hard code all the contents in PHP and then output into the Word document. I strongly discourage this due to the following reasons:
- There will be no flexibility. Any change in the output will require modification of the PHP script.
- It violates the separation between control and presentation.
- It will drastically increase the lines of code if we are to apply styles to the document contents (alignment, font, style, etc). Programmatically changing styles is too cumbersome.
Another way is to do a “search-replace”. PHP has strong built-in capabilities in doing this. We can create a Word document putting some special delimiters around the placeholder contents that are to be replaced. For example, we can create a document containing something like this:
{{name}}
and in PHP, we can simply replace this with the “Name” value we retrieved from the form submission.
This is straightforward and avoids all the disadvantages in the first option. We just need to find the right delimiter, and in this case, we are more like doing a template rendering, except that the template used is now a Word document.
The third option is my recommendation and is an advanced topic in Word. We will use fields to represent the placeholders, and in our PHP code, we will directly update the fields with respective form values.
This approach is flexible, fast and conforms with Word’s best practices. It also avoids full text search in the documents, which helps performance. Note that this option has its drawbacks too.
Word, ever since its debut, has never supported named indexes for fields. Even though we provided a name for the fields we created in the Word document, we still have to use number subscripts to access each field. This also explains why we have to use a dedicated function (setupfields
) to do the manual mapping between the field index and the name of the form fields.
To learn how to insert fields in a Word document (click here for a ready-made version), please consult the relevant Word help topics and manuals. For this demo, we have a document with 5 MERGEFIELD
fields. Also, we placed the document in the same directory as the PHP script for easy access.
Please note, the field printdate
does not have a corresponding form field. That is why we added a dummy printdate
key to the $inputs
array. Without this, the script can still run but there will be notice saying that the index printdate
is not presented in the $inputs
array.
After updating the fields with form values, we will print the document using:
$d->PrintOut();
The PrintOut
method has a few optional parameters and we are using its simplest form. This will print one copy to the default printer connected to our Windows machine.
We can also choose to use PrintPreview
to take a look at the output before we decide to print the document. In a purely automated environment, we will of course use PrintOut
instead.
We have to wait for a few seconds before we quit the Word application because the printing job needs some time to be fully spooled. Without delay(3)
, $w->Quit
gets executed immediately and the printing job gets killed too.
Finally, we call $w->Quit(false)
to close the Word application invoked by our PHP script. The only parameter provided here is to specify if we want to save changes before quitting. We did make changes to the document but we really don’t want to save them because we want to keep a clean template for other users’ input.
After we complete the code, we can load the form page, input some values and submit the form. The below images show the output of the PHP script and also the updated Word document:
Improving the coding speed and understanding more about PIA
PHP is a weakly typed language. A COM object is of type Object
. During our PHP coding, there is no way to get a meaningful code insight out of an object, be it a Word Application, a Document, or a Field. We don’t know what properties it has, or what methods it supports.
This will greatly slow down our development speed. To make it faster, I would recommend we develop the functions in C# first and then migrate the code back to PHP. A free C# IDE I would recommend is called “#develop” and can be downloaded here. I prefer this one to the VS series because #develop is smaller, cleaner, and faster.
The migration of C# code to PHP is not scary at all. Let me show you some lines of C# code:
Word.Application w=new Word.Application();
w.Visible=true;
String path=Application.StartupPath+"\template.docx";
Word.Document d=w.Documents.Open(path) as Word.Document;
Word.Fields flds=d.Fields;
int len=flds.Count;
foreach (Word.Field f in flds)
{
f.Select();
int i=f.Index;
w.Selection.TypeText("...");
}
We can see that C# code is almost identical to the PHP code we showed previously. C# is strongly typed so we see a few type casting statements and we have to explicitly give our variables a type.
With variable type given, we can enjoy code insight and code completion so the development speed is much faster.
Another way to speed up our PHP development is to tap on Word macros. We perform the same actions we need to do and record them with a macro. The macro is in Visual Basic, which can also be easily transformed to PHP.
Most importantly, Microsoft’s official documentation on Office PIA, especially the namespace documentation for each Office applications, is always the most detailed reference material. The mostly used three applications are:
- Excel 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel(v=office.15).aspx
- Word 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.word(v=office.15).aspx
- PowerPoint 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint(v=office.15).aspx
Conclusion
In this article, we demonstrated how to populate a Word document using PHP COM libraries and Microsoft Office Interop capabilities.
Windows and Office are widely used in everyday life. To have knowledge on the power of both Office/Windows and PHP will be essential for any PHP + Windows programmers.
With PHP’s COM extension, the door to mastering this combination is opened.
If you are interested in this area of programming, please leave your comments and we will consider having more articles on this topic. I look forward to seeing more real world applications developed using this approach.
/word/document1.doc
is the path for a file in the server, not in the client. On Windows, supposing that the file sharing is enabled for the client PC, then you can use a path such as \IPworddocument1.doc
, where IP is the IP of the client PC.
You can get the IP of the PC connecting to the server with $_SERVER['REMOTE_ADDR']
; $_SERVER['REMOTE_HOST']
is the result of a DNS reverse lookup, which could return the same value of $_SERVER['REMOTE_ADDR']
, in your case.
Probably PHP will not open remote files if it has not been set to do so (there is a directive or that).
If directly accessing the shared file from the COM object doesn’t work, then you can copy the file from the client PC to the server in a temporary file, and then give that file to the COM object. In this way, if there are any errors while accessing the networked file, you should be able to get them.
I find strange, anyway, that passing a network file path you get a local file. Are you sure the COM object is not copying of the server the file it finds at the remove file path passed? Did you try with a different file? If that happens with different files too, then we are missing something; I would find strange that for all the network files you try to open, there is already a local file with the same name. Try also renaming the network files.