Req #34094 [Opn]: COM hook into PHP class requires class var
| From: | girgias@php.net | Date: | Fri, 18 Oct 2019 11:46:21 +0000 |
| Subject: | Req #34094 [Opn]: COM hook into PHP class requires class var | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-223248@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=34094&edit=1
ID: 34094
Updated by: girgias@php.net
Reported by: csaba at alum dot mit dot edu
Summary: COM hook into PHP class requires class var
Status: Open
Type: Feature/Change Request
Package: COM related
Operating System: Windows
-PHP Version: 5CVS-2005-08-12 (dev)
+PHP Version: Next Minor Version
-Assigned To:
+Assigned To: cmb
Block user comment: N
Private report: N
New Comment:
Assigning to cmb for review as he's the new maintainer for COM.
Previous Comments:
------------------------------------------------------------------------
[2005-08-12 21:55:16] csaba at alum dot mit dot edu
I'm sure most of what I'm going to write below is already clear to you, but better to err
with too much info. If it's still lacking, please let me know. I've only tried this on
Win XP Pro, SP 2 and it's to be run with CLI php. the popup function is so that I get a MsgBox
window to the foreground to help with debugging. The calls to popup in the code below are there
just to illustrate execution order.
The first block of code right after the class definition instantiates IE and stuffs the web page.
It is now set up, except for hooking in the event handlers.
The next block of 3 lines sets up variable space within IE ($window is the window object within
IE's DOM). Without it, IE doesn't like foreign apps setting values on its window.varName
The next block sets up the event handlers. The first line where the class is created is so that IE
has a local copy of this PHP object. (In VB, I would assign an instance of the class that contains
the event handler function to the event handler (eg.
window.document.getElementById('domElemId').onclick=vbClassInstance When that event is
fired, the previously designated default function within the class (thus I'd need three classes
for the three event handlers) would be called). Trying the same type of assignment that VB uses did
not work however.
Now the hooking up happens in the next three lines (the third one (escape key detecter) being an
extended version for illustrative purposes of showing what happens with additional arguments).
The first one expands like so:
$window->ExecScript("document.getElementById('btnOK').onclick=function(){window.evtHandler.dummyVar('clickOK')}")
The point of that line is so that the string will execute as javascript local to the browser. The
reason I needed to do that was so I could assign an anonymous function (the function(){...}) to the
event handler, and the reason I needed to that was so that window.evtHandler didn't get
referenced till an event fired, else it would error or try to execute code. Thus, I asked IE to do:
document.getElementById('btnOK').onclick=function(){window.evtHandler.dummyVar('clickOK')}
So when the button gets clicked and the .onclick fires, the ... part of function() {...} is executed
in the context of the element clicked on (that means that 'this' is set to the button
element - but we don't make use of that here)
Thus, when the button is clicked we execute:
window.evtHandler.dummyVar('clickOK')
Now we are at the reason for the bug report. It looks like .dummyVar is being called like a
function (The bug. Furthermore, if I replaced .dummyVar with .clickOK or any other function from
the class I'd get an error). Instead, the last defined function within the evtHandler's
class is called (this is conceivably correct here since VB uses this paradigm (and it's not
even distressing since I can pass arguments), but it is an issue in the other bug (33386)), namely
__construct. Furthermore, that 'clickOK' is passed in as an argument (VERY cool. VB does
not let us pass arguments to the default function in this context). In all the "calls" to
dummyVar, my convention is that the first argument passed is the name of the real function within
the evtHandler class that I want executed. That's really what I would much rather replace
'dummyVar' with. Ie. I'd rather have window.evtHandler.clickOK() than
window.evtHandler.dummyVar('clickOK')
On the receiving end, __construct is called when the class is first created, but with no arguments,
so we filter out this first invocation by checking the number of arguments. Every subsequent call
from IE, with the silly .dummyVar style, will have the first argument indicating the real function
that we want to handle the event. The rest of the arguments (by my convention) are destined for the
real intended event handler function named by the first argument (thus $argsRest). Then the next
line makes the actual call. Note that I use "".func_get_arg(0) as the name of the
function to call. That is because that 'string' (func_get_arg(0)) is coming from
javascript and PHP views it as an object so I've got to coerce it or else call_user_func_array
is not happy.
Finally, three comments.
(1) The printing of the $ie->hwnd is simply to let me know that PHP is alive and still in the
loop.
(2) The code above works. Run it with php.exe and the browser (IE) should come up and you should
be able to click the buttons and get popups indicating what you did. This is all working really
well for me and there is no indication to the viewer that there are any problems. The reason for
filing the report is that the use of that .dummyVar indicates to me that something funny is going on
under the hood, and I'd rather have it taken away now, if that's going to happen, than to
suddenly realize a year later that I've been using a loophole. But really, this is a fabulous
technique so I hope the main pieces stay as is.
===> Just to be clear, the above code accomplishes everything (in fact more than) I wanted.
But it seems incorrect to me that attempting to access a variable of the class (with arguments, no
less), will cause a seemingly unrelated function of the class to be called, whereas attempting to
access that function (or any other) directly will result in an error. <===
(3). There was no real cause to have __construct be the final function. If it turns out that it is
only a single, final function that can be called as an event handler, then a developer should use a
separate one for that purpose and not mix in class creation. __contruct being last in my code is an
artifact left over from my initial testing when I had a single popup in __construct and
couldn't figure out why the class instance kept getting recreated each time I tried to access
it...
Thanks,
Csaba
------------------------------------------------------------------------
[2005-08-12 20:01:16] wez@php.net
Can you clarify your sample script?
I've been staring at it for 5 minutes and can't figure it out. I understand that
you're trying to call into PHP objects from the browser, but can't tell quite what
you're doing and what you expect it should be doing instead.
------------------------------------------------------------------------
[2005-08-12 11:49:07] csaba at alum dot mit dot edu
Description:
------------
This report seems related to http://bugs.php.net/bug.php?id=33386 but as far as I
can tell a distinct phenomenon is occurring. In this report, there is also only external access to
the last function, (but in the VB analogue, that is OK here as opposed to there) but the access here
is through a var of the class (as opposed to the class itself or a function of the class) and that
seems wrong.
Let me summarize the VB analagous situation, the acutal situation, and then the distinction between
the report above and this one.
1. Summary of the VB situation: In VB, documentation says and testing confirms that if you connect
a DOM event handler directly to a class built with VB in an .OCX, then if you have marked one of the
functions as default within the class (using Tools / Procedure Attributes / Advanced ... note that
this means one event handler per class), that is the one that will be executed when the event occurs
(note that this differs from the ScriptControl approach in the prior report where we expect access
to all the class functions). An example connection is made by:
IE.Document.getElementById('elemId').onclick=classInstance
Note also that there is no facility for passing an argument to the class.
2. Summary of actual situation: With PHP in this report, we have to set a variable in the DOM to
the class instance (OK to here) and then to access the class (here comes the bug:), ===> we must
name a var in the class <=== (as opposed to the class instance itself or the function we want to
access or for that matter (as in the earlier report) any function). At least we are able to pass
arguments.
3. Comparison with prior report: In both reports, we are only able to access (from an external COM
object) the last function in the class, but in the earlier report that was not expected (in
comparison to VB) whereas it is conceivable here. In the earlier report, to access that last
function any function of the class had to be named whereas here (and the reason for this report), a
var (and not a function) of the class needs to be named. Frankly, I would expect that other
functions are accessible since some external agent (PHP? COM? javascript?) is discriminating based
on what has been named.
Thanks for considering this,
Csaba Gabor from Vienna
Reproduce code:
---------------
The code below (watch for possible line wrap) manages to successfully hook a PHP class to event
handlers within the IE DOM (as opposed to event handlers of the IE COM object). See following
section for issues.
<?
function popup ($text, $title="PHP popup",
$timeout=4, $style=131120) {
$oWSH = new COM("WScript.Shell");
$oWSH->Popup($text, $timeout, $title, $style); }
function hookHandler ($wnd, $elemId, $hndlrName) {
$wnd->ExecScript(
"document.getElementById('$elemId')." .
"onclick=function(){window.evtHandler." .
"dummyVar('$hndlrName')}"); }
class evtHandler {
function clickOK() { popup ("OK was clicked"); }
function clickBtn2() { popup ("Test click"); }
function keyPress($window) {
if ($window->event->keyCode==27) // ESC
$window->doneP=true; }
var $dummyVar;
function __destruct() { popup ("destruction"); }
function __construct() {
if (func_num_args(0)) {
popup("in event handler __construct: ");
$argsRest = array_slice(func_get_args(),1);
call_user_func_array (array(&$this,
"".func_get_arg(0)), $argsRest);
} else popup("initialization __construct");
}
}
$ie = new COM("InternetExplorer.Application");
$ie->visible = true;
$ie->Navigate2("about:blank");
$body ="<button id=btnOK " .
"accesskey=O><u>O</u>K</button> " .
"<button id=btn2 " .
"accesskey=T><u>T</u>est me</button> " .
"<button accesskey=c " .
"onclick='window.doneP=true'>" .
"<u>C</u>ancel</button>";
$ie->Document->body->innerHTML .= $body;
$window = $ie->Document->parentWindow;
$window->ExecScript ("window.doneP=false");
$window->ExecScript ("window.evtHandler=false");
$window->evtHandler = new evtHandler();
hookHandler ($window, 'btnOK', 'clickOK');
hookHandler ($window, 'btn2', 'clickBtn2');
$window->ExecScript("document.onkeypress=function(){" .
"window.evtHandler.dummyVar('keyPress',window)}");
try { while (!$window->doneP) {
com_message_pump(600);
print "\n" . $ie->hwnd; }
$ie->Quit(); }
catch (Exception $e) { popup ("IE has gone away"); }
popup("Done with php program");
?>
Expected result:
----------------
I expect to name the (non private) function within the class that I'd like to call for a DOM
event handler, and have that function be executed. If it turns out (because of COM restrictions et
al) that an arbitrary function can't be called, then I would expect to have to name exactly the
function that will be called and not an arbitrary variable (dummyVar) within the class.
Actual result:
--------------
1. I need to refer to dummyVar on lines 10, 50
2. All calls (attempt to) go to the final function defined, even if it's marked private (in
this latter case, the call would fail)
I REALLY like being able to pass arguments to PHP event handlers (e.g. pressing the Escape key in
this example), thanks for building it in. I hope that is here to stay.
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=34094&edit=1