Page 1 of 3
Dynamic invocation of constructors...
Posted: Mon May 08, 2006 1:07 pm
by nielsene
I'm running into a situation where I need dynamic invocation of a constructor with an unkown number of arguments.
That is I don't know either the class name or the arugment list at script-writing time. (I would say compile time, but....)
Normally I use call_user_func_array() for this, but it appears that it doesn't work with constructor type functions. There's always eval, but I would prefer to avoid that. Is there any other workaround?
If I knew the argument list at script writing time, I could just do a "new $classname($arg1, $arg2,...)" However, I need the rich constructor and can't fallback to a no-args constructor and lots of setFoo calls.
Posted: Mon May 08, 2006 2:10 pm
by Christopher
func_get_arg -- Return an item from the argument list
func_get_args -- Returns an array comprising a function's argument list
func_num_args -- Returns the number of arguments passed to the function
For the class you probably need some sort of Dispatcher or Dynamic Linkage.
Posted: Mon May 08, 2006 2:20 pm
by nielsene
The different classes don't take variable arguments so the constructor doesn't need to use those functions. I have code to correctly assemble the arguements to pass to the chosen class/constructor, but I have no way to invoke it short of eval.
Posted: Mon May 08, 2006 2:25 pm
by Christopher
Take a look a the Builder and Factory patterns, and maybe the Proxy pattern.
Posted: Mon May 08, 2006 3:14 pm
by nielsene
I know those patterns; they don't apply here.
I give up. As always, it seems I can't ask questions that lead to answers.
Posted: Mon May 08, 2006 3:19 pm
by Roja
nielsene wrote:I know those patterns; they don't apply here.
I give up. As always, it seems I can't ask questions that lead to answers.
As much as I dislike the site, the tenor, the members, and the overall environment, these questions I find are often better answered at Sitepoint's php forums. Perhaps try asking there.
Posted: Mon May 08, 2006 3:39 pm
by Christopher
nielsene wrote:I know those patterns; they don't apply here.
I give up. As always, it seems I can't ask questions that lead to answers.
I think you need to describe it better then. It almost sounds like you need something like the WACT Handle class that resolves classes. You haven't said what the purpose is. Lazy Load, Virtualize?
You started out saying "with an unkown number of arguments", then you said "The different classes don't take variable arguments." If you talk through the problem you may discover, for yourself, whether you should be solving it in this way or not. I am certainly not clear what you are trying to do.
Posted: Mon May 08, 2006 4:32 pm
by nielsene
OK I'll try again, but I don't expect it to help, since people will just say my approach is wrong.
In the middle of my ORM layer, I often need to instantiate a class that requires a rich constructor. The mapping files identifies the class needed and a "template" constructor argument list. Thus I can build the argument list that the chosen constructor needs. However short of eval, I can't seem to find anyway to call the constructor with the appropriate arguments.
Different classes being loaded by the ORM might need different number of arguments, thus I can't do something like "new $foo($arg1, $arg2, $arg3)";
In this case the object being loaded is immutable and I have to create it from the rich constructor, thus no JavaBean style convention of a no-args constructor followed by lots of setX calls. The ORM layer is designed to impose NO requirements on the classe to be persisted -- it does require a more complicated mapping syntax, but it doesn't corrupt the domain model.
Basically I need a
Code: Select all
$obj = call_user_func(array($className,'__construct'),$args);
However call_user_func can't be used to invoke a constructor according to the documentation (and confirmed by trying it anyways.)
Posted: Mon May 08, 2006 4:45 pm
by Christopher
It sound like you need eval(). There is nothing wrong with resorting to eval() when needed. Here is some
lastcraft code from his changes library that may be inspiration:
Code: Select all
<?php
/**
* $Id: invoker_class.php,v 1.8 2004/11/09 17:32:36 marcusbaker Exp $
* Calls an object method with reference parameters and returns the
* result by reference. Replaces call_user_func_array().
* @copyright http://www.wordtracker.com (Rivergold Associates Ltd)
* @author Mike Mindel & Marcus Baker & Peter Brown
* @package classes
*/
/** @ignore */
/**
* This class is currently just a method to call other objects with the
* parameters as an array.
* @package classes
*/
class Invoker {
/**
* Service.
* @access public
* @param array $parameters Reference passed array of parameters.
*/
function Invoker() {
}
/**
* Calls an object and passes the array of parameters by reference.
* @access public
* @param object $object Object to call on.
* @param string $method Method name.
* @param array $parameters Array of parameters passed by reference.
* @return mixed Reference return of method result.
*/
function &invokeMethod(&$object, $method, &$parameters) {
$aliases = array();
for ($i = 0; $i < count($parameters); $i++) {
$aliases[] = "\$parameters[$i]";
}
$code = '$result = &$object->' . $method . '(' . implode(', ', $aliases) . ');';
eval($code);
return $result;
}
/**
* Calls a static method and passes the array of parameters by reference.
* @access public
* @param string $class Class to call on.
* @param string $method Method name.
* @param array $parameters Array of parameters passed by reference.
* @return mixed Reference return of method result.
*/
function &invokeStaticMethod($class, $method, &$parameters) {
$aliases = array();
for ($i = 0; $i < count($parameters); $i++) {
$aliases[] = "\$parameters[$i]";
}
$code = '$result = &' . $class . '::' . $method . '(' . implode(', ', $aliases) . ');';
eval($code);
return $result;
}
}
?>
Posted: Mon May 08, 2006 4:58 pm
by nielsene
That's basically why I have already.
Posted: Mon May 08, 2006 5:23 pm
by Christopher
From what I can tell, the reason that you still have to do stuff like this is because the PHP core group have what I would consider an old fashioned view of OO. And unfortunately, there are really no plans for much improvement in the PHP object model because of this attitude. There are a number of features that, though they would rarely be used by application programmers, would help library builders create really interesting stuff in PHP.
Posted: Mon May 08, 2006 6:02 pm
by cj5
I'm no expert at it yet, but I do believe using PHP4, you've hit a raod block on this. Except if you go with arborint's suggestion, by using the factory patterns available in most PEAR packages. Cue, PHP5. I know from my initial experiences with PHP5 you can use the get_/set_ methods in conjunction. There's a great little tute on it
here. I hope that helps. Let me know what you find.
Posted: Mon May 08, 2006 6:19 pm
by Christopher
The problem with __get()/__set() is that they are error handlers -- not actually accessor functions. They are another example of problem library builders have with PHP. You really can't build things like an ORM layer that will "impose NO requirements on the classe to be persisted" as nielsene is finding. I have seen some of the best PHP programmers hit this wall ... even Zend failed with their own attempt at an Active Record implementation for the Zend Framework. It is a lack of vision that causes us all to suffer from a lack of high-powered libraries (things like Hibernate) in PHP.
Posted: Mon May 08, 2006 7:20 pm
by alex.barylski
Roja wrote:nielsene wrote:I know those patterns; they don't apply here.
I give up. As always, it seems I can't ask questions that lead to answers.
As much as I dislike the site, the tenor, the members, and the overall environment, these questions I find are often better answered at Sitepoint's php forums. Perhaps try asking there.
For what reason would that be?
Whats wrong with this place?
Posted: Mon May 08, 2006 7:41 pm
by Roja
Hockey wrote:Roja wrote:nielsene wrote:I know those patterns; they don't apply here.
I give up. As always, it seems I can't ask questions that lead to answers.
As much as I dislike the site, the tenor, the members, and the overall environment, these questions I find are often better answered at Sitepoint's php forums. Perhaps try asking there.
For what reason would that be?
Whats wrong with this place?
What reason would what be? That I dislike the site, the tenor, the members, and the overall environment at Sitepoint? Answering that would take the thread very very off-topic.
There is nothing wrong with this place. My point was that despite my dislike for Sitepoint, I find that the answers to OOP questions there are generally more robust than they are here.