How to pass a set of variables to a class - php

This may be a bit of an XY questions, so I'm going to explain what I'm trying to do first. I'm attempting to create a single php file to handle all of my page refresh AJAX calls. That means I want to be able to send it a class name, plus a list of the variables that the class constructor takes, and for it to then create the class.
I can create the class fine. $class = new $className(); works just fine for creating the class. The problem is passing in the default variables. Most of the variables are objects containing other classes, so I can't just include this once the class is created, I need to pass them as the class is created.
I was thinking something along the lines of:
$varStr = '';
$s = '';
foreach($vars as $var) {
switch($var['type']) {
case 'object':
$varStr .= $s . '$' . $var['value'];
break;
case 'variable':
$varStr .= $s . $var['value'];
}
$s = ',';
}
$class = new $className(echo $varStr);
Now obviously echo $varStr isn't going to work there, but I have no idea what will. Is there anything I can do that will output the variables from my array into the class constructor like that? Is what I'm trying to do even possible? Is there a better way?
Whilst I understand I could just pass the whole array to the class constructor, this would complicate the main part of the program, and I would rather just ditch the idea of a single page for AJAX refresh than go down that route.

So basically you're trying to pass a variable number of arguments to a constructor? In a regular function, you could do something like:
function foo() {
$args = func_get_args();
...
}
call_user_func_array('foo', array('bar', 'baz'));
This won't work for constructors, since the calling mechanism is different. You could do:
class Foo {
public function __construct() {
$args = func_get_args();
...
}
}
$class = new ReflectionClass('Foo');
$obj = $class->newInstanceArgs(array('bar', 'baz'));
But really, what you should be doing is this:
class Foo {
public function __construct(array $args) {
...
}
}
$obj = new Foo(array('bar', 'baz'));
or
class Foo {
public function __construct($bar, $baz) {
...
}
}
$obj = new Foo('bar', 'baz');
Anything else is quite insane. If your object constructor is so complicated, you probably need to simplify it.

This is a wild guess at what you're trying to do but maybe this is what you're after:
// Generate constructor args
$args = array();
foreach($vars as $var) {
switch($var['type']) {
$value = $var['value'];
case 'object':
args[] = ${$value}; // evaluate, I think that's what you want?
break;
case 'variable':
args[] = $value; // use as is
break;
}
}
// Instanciate class with args
$class = new ReflectionClass($className);
$obj = $class->newInstanceArgs($args);
For this to work, it would require $vars to enumerates args in the correct order expected by each class constructor.

Related

can I create a class dynamically with $? (dollar-sign)

In PHP sometimes it would be nice if I could define a function or a class with a variable name like
$myfunctionname="test";
function $myfunctionname(){
//...
}
so it would create the function test()
or with classes too like:
$foo = bar;
class $foo {
// lots of complicated stuff
// ...
}
but this doesen't work. like this it would give parse errors!
Is there a solution to this?
(I know, this is not good practise, but just as a workaround, it would be handy)
EDIT: My actual problem:
I have a framework with a migration process where every migration step is in a separate php include file in a folder.
Each file contains only one migration class that contains the name of the include file.
Because the class has to have that certain name, I would like to create the name of the class to a generic name that is created by the filename constant __FILE__
Yes, you can, but I dont want you to.
$classname = "test";
eval("class {$classname}{ public function hello(){ echo \"hello!\"; } }");
$evil_class = new $classname();
$evil_class->hello(); // echo's "hello!"
now, if you don't mind me I'm going for a shower.
You can use a factory pattern:
class poly_Factory {
public function __construct() {
$class = 'poly';
return new $class();
}
}
If that is anything you want to get to.
http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
Scroll down to step 4, last part...
I know you did not ask for that, but what can your question be good for else?
No. This code throws a parse error on line 3 because of the $:
$foo = 'bar';
class $foo {
function hello() {
echo "World";
}
}
$mybar = new bar();
$mybar->hello();
Result:
Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING on line 3
And as Jan Dvorak pointed out in the comments: even if you figure out a way to do this, don't do this.
If you want to create a value object you can just use the stdClass builtin type.
$object = new stdClass;
$object->someValue = "Hello World";
echo $object->someValue;
See it in Action
If you want to assign methods then you have to use the magic __call function, here is how I would do it.
class AnonObject{
private $properties = array();
private $methods = array();
public function __get($property){
return array_key_exists($property, $this->properties)?$this->properties[$property]:null;
}
public function __set($property, $value){
if (!is_string($value) && is_callable($value)){
if ($value instanceof \Closure){
// bind the closure to this object's instance and static context
$this->methods[$property] = $value->bindTo($this,get_class($this));
} else {
// invokable objects
$this->methods[$property] = $value;
}
} else {
$this->properties[$property] = $value;
}
}
public function __call($method, $args){
if (array_key_exists($method, $this->methods)){
call_user_func_array($this->methods[$method], $args);
} else {
throw new RuntimeException("Method ".$method." does not exist on object");
}
}
}
See it In Action
Note, as stated by several other people this is bad practice. If the goal of this exercise is to compose the behavior of an instance of an object at runtime a more maintainable solution would be to use the Strategy Pattern

ArrayAccess in PHP -- assigning to offset by reference

First, a quote from the ole' manual on ArrayAccess::offsetSet():
This function is not called in assignments by reference and otherwise indirect changes to array dimensions overloaded with ArrayAccess (indirect in the sense they are made not by changing the dimension directly, but by changing a sub-dimension or sub-property or assigning the array dimension by reference to another variable). Instead, ArrayAccess::offsetGet() is called. The operation will only be successful if that method returns by reference, which is only possible since PHP 5.3.4.
I'm a bit confused by this. It appears that this suggests that (as of 5.3.4) one can define offsetGet() to return by reference in an implementing class, thus handling assignments by reference.
So, now a test snippet:
(Disregard the absence of validation and isset() checking)
class Test implements ArrayAccess
{
protected $data = array();
public function &offsetGet($key)
{
return $this->data[$key];
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key) { /* ... */ }
public function offsetUnset($key) { /* ... */ }
}
$test = new Test();
$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
// overloaded object in
var_dump($test, $bar);
Ok, so that doesn't work. Then what does this manual note refer to?
Reason
I'd like to permit assignment by reference via the array operator to an object implementing ArrayAccess, as the example snippet shows. I've investigated this before, and I didn't think it was possible, but having come back to this due to uncertainty, I (re-)discovered this mention in the manual. Now I'm just confused.
Update: As I hit Post Your Question, I realized that this is likely just referring to assignment by reference to another variable, such as $bar = &$test['foo'];. If that's the case, then apologies; though knowing how, if it is at all possible, to assign by reference to the overloaded object would be great.
Further elaboration: What it all comes down to, is I would like to have the following method aliases:
isset($obj[$key]); // $obj->has_data($key);
$value = $obj[$key]; // $obj->get_data($key);
$obj[$key] = $value; // $obj->set_data($key, $value);
$obj[$key] = &$variable; // $obj->bind_data($key, $variable);
// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);
unset($obj[$key]); // $obj->remove_data($key);
As far as has, get, set, and remove go, they're no problem with the supported methods of ArrayAccess. The binding functionality is where I'm at a loss, and am beginning to accept that the limitations of ArrayAccess and PHP are simply prohibitive of this.
What the manual is referring to are so called "indirect modifications". Consider the following script:
$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';
In the above script $array['foo'] = array(); will trigger a offsetSet('foo', array()). $array['foo']['bar'] = 'foobar'; on the other hand will trigger a offsetGet('foo'). Why so? The last line will be evaluated roughly like this under the hood:
$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';
So $array['foo'] is first fetched by ref and then modified. If your offsetGet returns by ref this will succeed. If not you'll get some indirect modification error.
What you want on the other hand is the exact opposite: Not fetch a value by reference, but assign it. This would theoretically require a signature of offsetSet($key, &$value), but practically this is just not possible.
By the way, references are hard to grasp. You'll get lots of non-obvious behavior and this is especially true for array item references (those have some special rules). I'd recommend you to just avoid them altogether.
This does not work with ArrayAccess, you could add yourself a public function that allows you to set a reference to an offset (sure, this looks different to using array syntax, so it's not really a sufficient answer):
class Test implements ArrayAccess{
protected $_data = array();
public function &offsetGet($key){
return $this->_data[$key];
}
...
public function offsetSetReference($key, &$value)
{
$this->_data[$key] = &$value;
}
}
$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
Probably such a function was forgotten when ArrayAccess was first implemented.
Edit: Pass it as "a reference assignment":
class ArrayAccessReferenceAssignment
{
private $reference;
public function __construct(&$reference)
{
$this->reference = &$reference;
}
public function &getReference()
{
$reference = &$this->reference;
return $reference;
}
}
class Test implements ArrayAccess{
...
public function offsetSet($key, $value){
if ($value instanceof ArrayAccessReferenceAssignment)
{
$this->offsetSetReference($key, $value->getReference());
}
else
{
$this->_data[$key] = $value;
}
}
Which then works flawlessly because you implemented it. That's probably more nicely interfacing than the more explicit offsetSetReference variant above:
$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)

json_encode on class with magic properties

I am trying to json_encode an array of objects who all have magic properties using __get and __set. json_encode completely ignores these, resulting in an array of empty objects (all the normal properties are private or protected).
So, imagine this class:
class Foo
{
public function __get($sProperty)
{
if ($sProperty == 'foo')
{
return 'bar!';
}
return null;
}
}
$object = new Foo();
echo $object->foo; // echoes "foo"
echo $object->bar; // warning
echo json_encode($object); // "{}"
I've tried implementing IteratorAggregate and Serializable for the class, but json_encode still doesn't see my magic properties. Since I am trying to encode an array of these objects, an AsJSON()-method on the class won't work either.
Update! It seems the question is easy to misunderstand. How can I tell json_encode which "magic properties" exist? IteratorAggregate didn't work.
BTW: The term from the PHP documentation is "dynamic entities". Whether or not the magic properties actually exist is arguing about semantics.
PHP 5.4 has an interface called JsonSerializable. It has one method, jsonSerialize which will return the data to be encoded. This problem would be easily fixed by implementing it.
json_encode() doesn't "asks" the object for any interface. It directly fetches the HashTable pointer that represents the properties of an object by calling obj->get_properties(). It then iterates (again directly, no interface such as Traversable, Iterator etc. is used) over this HashTable and processes the elements that are marked as public. see static void json_encode_array() in ext/json/json.c
That makes it impossible to have a property to show up in the result of json_encode() but not to be accessible as $obj->propname.
edit: I haven't tested it much and forget about "high performance" but you might want to start with
interface EncoderData {
public function getData();
}
function json_encode_ex_as_array(array $v) {
for($i=0; $i<count($v); $i++) {
if ( !isset($v[$i]) ) {
return false;
}
}
return true;
}
define('JSON_ENCODE_EX_SCALAR', 0);
define('JSON_ENCODE_EX_ARRAY', 1);
define('JSON_ENCODE_EX_OBJECT', 2);
define('JSON_ENCODE_EX_EncoderDataObject', 3);
function json_encode_ex($v) {
if ( is_object($v) ) {
$type = is_a($v, 'EncoderData') ? JSON_ENCODE_EX_EncoderDataObject : JSON_ENCODE_EX_OBJECT;
}
else if ( is_array($v) ) {
$type = json_encode_ex_as_array($v) ? JSON_ENCODE_EX_ARRAY : JSON_ENCODE_EX_OBJECT;
}
else {
$type = JSON_ENCODE_EX_SCALAR;
}
switch($type) {
case JSON_ENCODE_EX_ARRAY: // array [...]
foreach($v as $value) {
$rv[] = json_encode_ex($value);
}
$rv = '[' . join(',', $rv) . ']';
break;
case JSON_ENCODE_EX_OBJECT: // object { .... }
$rv = array();
foreach($v as $key=>$value) {
$rv[] = json_encode((string)$key) . ':' . json_encode_ex($value);
}
$rv = '{' . join(',', $rv) .'}';
break;
case JSON_ENCODE_EX_EncoderDataObject:
$rv = json_encode_ex($v->getData());
break;
default:
$rv = json_encode($v);
}
return $rv;
}
class Foo implements EncoderData {
protected $name;
protected $child;
public function __construct($name, $child) {
$this->name = $name;
$this->child = $child;
}
public function getData() {
return array('foo'=>'bar!', 'name'=>$this->name, 'child'=>$this->child);
}
}
$data = array();
for($i=0; $i<10; $i++) {
$root = null;
foreach( range('a','d') as $name ) {
$root = new Foo($name, $root);
}
$data[] = 'iteration '.$i;
$data[] = $root;
$root = new StdClass;
$root->i = $i;
$data[] = $root;
}
$json = json_encode_ex($data);
echo $json, "\n\n\n";
$data = json_decode($json);
var_dump($data);
There is at least one flaw: It doesn't handle recursion, e.g.
$obj = new StdClass;
$obj->x = new StdClass;
$obj->x->y = $obj;
echo json_encode($obj); // warning: recursion detected...
echo json_encode_ex($obj); // this one runs until it hits the memory limit
From your comment:
I am asking about the magic properties, not the methods. – Vegard Larsen 1 min ago
There is no such thing as a magic property.
All you have right now is a magic method that gets called when you try to access a non-visible property.
Let's say it again
$obj->foo does not exist
The way a magic method is implemented is not a concern of PHP, and it can't magically know that you are using your magic method to 'magically' make it look like there is $obj->foo.
If a property does not exist, it will not be put into the object when you json_encode it.
Furthermore, even if json_encode knew that __get was active, it wouldn't know what value to use to call it.
This is an incredibly old thread, but to not confuse other posters who are interested in how magic methods are used to get/set properties in PHP and how that affects JSON encoding, here is my understanding.
Including __get and __set methods in a class give you an interface to deal with dynamically named properties for a given class. Most people define an internal associative array that does the housekeeping.
The reason your foo/bar example doesn't show up in JSON encoding is because you are simply creating an association that returns a value. It's not setting a property in the object itself to that value.
If you did something like:
public function set($name, $value) {
$this->data[$name] = $value;
}
Then if you called :
$foo = new ObjectClass;
$foo->set('foo','bar');
now there is a element in that array $data['foo'] = 'bar'
if you json_encode the object, then that relationship will be represented.
json_encode does not encode methods, only properties. There's no way around it. For your code, the functional equivalent of your magic __get, would be to include in the constructor a call to set, that "hardwires" the value of the property named "foo".
Ultimately, I don't know what specifics you are wrestling with as you didn't provide much in the way of detail. However, json_encoding a object or an array will simply give you a list of properties that are available. If you have to pass through an interpreting function and are relying on that somehow, that's a different problem that I unfortunately have no answer for.
I hope this helps even though it is part of a downrated thread, which ironically, IMO, contains the solution.
I'd create a method on the object to return the internal array.
class Foo
{
private $prop = array();
public function __get($sProperty)
{
return $this->prop[$sProperty];
}
public function __set($sProperty, $value)
{
$this->prop[$sProperty] = $value;
}
public function getJson(){
return json_encode($this->prop);
}
}
$f = new Foo();
$f->foo = 'bar';
$json = $f->getJson();
This is the correct behavior
JSON is only able to contain data not methods - it is meant to be language independent so encoding object methods would not make sense.
How could it know your properties?
$object = new Foo();
echo $object->foo; // how do you know to access foo and not oof?
// how do you expect json_encode to know to access foo?
magic methods are syntactic sugar, and mostly fire back when you use them. this is one such case.
echo json_encode($object); // "{}"
of course it's empty, $object has zero public properties, just a magic method and the ensuing syntactic sugar (turned sour)
i have found the following that worked for me creating classes with dynamic properties and outputing them with json_encode. Maybe it helps other people to.
http://krisjordan.com/dynamic-properties-in-php-with-stdclass

Pass functions to a class

PHP
mysql database
I have created a follow on question to this one here that is specifically about pagination
I need to call a method from one class in another, and be able to change the method that is called. Like so
class db{
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class YourClass {
var $fcn;
$db = new db()
function invoke(){
call_user_func($this->fcn);
}
}
$instance = new YourClass;
$instance->fcn = 'db->a';
$instance->invoke();
I want to use a method 'a' from the db class in the 'yourClass' method 'invoke'
Thanks
Ok this is what i have put together from the answers provided and it works.
class A {
function a(){
$x = 'Method a is used';
return $x;
}
function b(){
$x = 'Method b is used';
return $x;
}
}
class B {
function invoke($obj, $method){
echo call_user_func( array( $obj, $method) );
}
}
$instance = new B();
$instance->invoke(new A(),"a");
Which writes, 'Method a is used' to the screen
But i really want to be able to pass arguments to method "a" so i tried the code below.
class A {
function a($var1,$var2,$var3){
$x = 'the three passed values are ' . $var1 . ' and ' . $var2 . ' and ' . $var3;
return $x;
}
function b(){
$x = 'im method b';
return $x;
}
}
class B {
function invoke($obj,$arguments){
echo call_user_func_array($obj,$arguments);
}
}
$arguments = array('apple','banana','pineapple');
$use_function = array(new A(),"a");
$instance = new B();
$instance->invoke($use_function,$arguments);
It almost works but i get these errors above the correct answer
Missing argument 1 for A::a(),.....for argument 2 and 3 as well but then the answer prints to the screen
"the three passed values are apple and banana and pineapple"
I'm probably making a rookie mistake I've been coding all day. If someone could fix the script above and submit the working code, I would be eternally grateful. I have to put this issue to bed so i can go to bed.
Thanks
As of PHP5.3 you could use closures or functors to pass methods around. Prior to that, you could write an anonymous function with create_function(), but that is rather awkward.
Basically, what you are trying to do could be done with the Strategy Pattern.
removed example code, as it wasn't helpful anymore after the OP changed the question (see wiki)
Apart from that, you might want to look into Fowlers's Data Source Architectural Patterns. The Zend Framework (and pretty much all other PHP frameworks) offers database access classes you could use for these patterns and there is also a paginator class, so why not check them out to learn how they did it.
removed EDIT 1 as it wasn't helpful anymore after the OP changed the question (see wiki)
EDIT 2
Ok, let's take a step by step approach to this (not using a Strategy Pattern though)
What you are asking for in the question can easily be solved with this code:
class Foo
{
public function bar()
{
echo 'bar method in Foo';
}
}
class MyInvoker
{
protected $myObject;
public function __construct()
{
$this->myObject = new Foo();
}
public function __call($method, $args)
{
$invocation = array($this->myObject, $method);
return call_user_func_array($invocation, $args);
}
}
With this code you'd just call the appropriate methods. No setting of methods names. No clumsy extra invoke method. No reinventing of how methods are called. You dont need it, because PHP has the __call function that you just taught to send all methods not existing in MyInvoker to $myObject, e.g. Foo.:
$invoker = new MyInvoker;
$invoker->bar(); // outputs 'bar method in Foo called'
You might just as well have extended MyInvoker to be a subclass of Foo, e.g.
class MyInvoker extends Foo {}
and then you could do the same. This not what you need though and it illustrates how pointless it is, to do such a thing. MyInvoker now does nothing by itself. It is an empty class and effectively the same as Foo. Even with the previous approach using the __call method it is not doing anything. This is why I have asked you to be more specific about the desired outcome, which is a Paginator.
First try:
class Paginator()
{
// A class holding all possible queries of our application
protected $queries;
// A class providing access to the database, like PDO_MySql
protected $dbConn;
public function __construct()
{
$this->db = new MyPdo();
$this->queries = new DbQueries();
}
public function __call($method, $args)
{
$invocation = array($this->queries, $method);
$query = call_user_func_array($invocation, $args);
return $this->dbConn->query($query);
}
}
With that code, our Paginator creates everything it needs inside itself, tightly coupling the db connection class and all queries and it allows you to call upon these, like so
$paginator = new Paginator;
// assuming we have something like getImageCount() in DbQueries
echo $paginator->getImageCount();
What is happening then is, Paginator will recognize it doesnt know getImageCount() and will invoke the __call method. The __call method will try to invoke the getImageCount() method on the DbQueries. Since it exists, it will return the query, which in turn is passed to the db connection to execute it. Great you'd say, but it's not. In fact, this is horrible. Your paginator's responsibility is to count items in a table and fetch items from this table in a certain range and amount. But right now, it is not doing anything like this. It is completely oblivious to whats going on, so lets try a new class:
class Paginator
{
protected $dbConn;
protected $itemCount;
public function __construct($dbConn)
{
$this->dbConn = $dbConn;
}
public function countItems($query)
{
$this->itemCount = $this->dbConn->query('select count(*) from (?)', $query);
return $this->itemCount;
}
public function fetchItems($query, $offset = 0, $limit = 20)
{
$sql = sprintf('select * from (?) LIMIT %d, %d', $offset, $limit);
return $this->dbConn->query($sql, $query);
}
}
Much better. Now our Paginator is an aggregate instead of a composite, meaning it does not instantiate objects inside itself, but requires them to be passed to it in the constructor. This is called dependency injection (and also provides a loose coupling, when dbConn uses an interface) which will make your app much more maintainable, as it is easy to exchange components now. This will also come in handy when Unit Testing your code.
In addition, your Paginator now concentrates on what it is supposed to do: counting and fetching items of an arbitrary query. No need to pass methods around. No need for obscure method invocation. You'd use it like this:
$paginator = new Paginator($dbConn);
$query = $dbQueries->findImagesUploadedLastWeek(); // returns SQL query string
$images = $paginator->countItems($query);
if($images > 0) {
$images = $paginator->fetchItems($query);
}
And that's it. Well, almost. You'd have to render the pagination of course. But this should be rather trivial, if you extend what you already have above. The $imageCount property is a hint at where to go next.
Anyway, hope that I could shed some light.
P.S. The $this->dbConn->query($sql, $query) calls are of course dummy code. Dont expect to be able to copy and paste it and get it working. In addition, you should make sure the queries added to the Paginator SQL is safe to use. You wouldnt want someone to insert a query that deletes all your db rows. Never trust user input.
P.P.S. $query should be an SQL query string. Check the PHP manual for PDO::prepare. In general, it yields better performance and security to prepare a statement before executing it. The page in the manual will give you the clues about the ? in the query calls. If you dont want to use PDO, just use sprintf() or str_replace() to replace ? with $query, e.g. $this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query) but keep in mind that this has none of the benefits of a prepared statement and potentially opens the door for SQL Injection vulnerabilities.
P.P.P.S Yes, Dependency Injection is generally a preferred strategy. This is an advanved topic though and might be too much to fully grasp right now, but it's well worth looking into it. For now, it should be enough if you try to favor favor aggregation over composition. Your classes should only do what they are responsible for and get any dependencies through the constructor.
Here are two ways of doing it:
class YourClass {
var $fcn;
function invoke($arguments){
//one way:
$this->{$this->fcn}($arguments);
//another way:
call_user_func_array(array($this, $this->fcn), $arguments);
}
function a(){
echo 'I am a()';
}
}
$instance = new YourClass;
$instance->fcn = 'a';
$instance->invoke();
This'll print out "I am a()" from inside the class.
you are almost there
class db {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class YourClass {
var $fcn;
function __construct() {
$this->db = new db();
}
function invoke() {
call_user_func(array(
$this->{$this->fcn[0]},
$this->fcn[1]
));
}
}
$instance = new YourClass;
$instance->fcn = array('db', 'a');
$instance->invoke();
$instance->fcn = array('db', 'b');
$instance->invoke();
the syntax is quite fancy, but it works
// edit: from your comment it looks like the simplest option is to pass method name as string, like this
class Paginator {
function __consturct($db, $counter_func) ...
function get_count() {
$args = func_get_args();
return call_user_func_array(
array($this->db, $this->counter_func),
$args);
}
}
new Paginator($db, 'get_num_products');
I am guessing that you are using php here. Php supports variable functions which might solve you problem but as far as I am aware does not support delegates/function pointers.
What database are you using? I would be against putting queries within the code and using stored procedures as an alternative, if this is supported in the database you are using. This may solve the underlying problem you have.
Are you asking if PHP has functional references? It doesn't. But it does let you call functions by putting their name in a string, or an array of a class name and method name.
See call_user_func() for a description, and variable functions.
class DB {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class B {
protected $db;
private $method;
function __constructor($db) { $this->db; }
function invoke($m){
$this->method = $m;
// Non static call
call_user_func( array( $this->db, $this->method ) );
}
}
$db = new DB();
$b = new B($db);
$b->invoke('a');
I have made little modifications to my initial answer. You could also check out this post, it may help:
Database and OOP Practices in PHP
The Observer Design Pattern may be useful for this sort of thing, or it might be a misuse of the pattern; I don't know yet. Anyway, for your consideration:
class DbObserver implements SplObserver
{
public function update(SplSubject $subject) // Required
{
$method = $subject->getObserverMethod();
$args = $subject->getObserverArgs();
$this->$method($args);
}
private function a($args)
{
echo 'I run query ' . $args[0] . '<br />';
}
private function b($args)
{
echo 'I run query ' . $args[0] . ' because ' . $args[1] . '<br />';
}
private function c()
{
echo 'I have no argument' . '<br />';
}
}
class ObserverObserver implements SplObserver
{
public function update(SplSubject $subject) // Required
{
if (count($subject->getAttached()) > 1) {
echo 'I saw that<br />';
} else {
echo 'Nothing happened<br />';
}
}
}
class DbSubject implements SplSubject
{
private $observerMethod;
private $observerArgs = array();
private $attached = array();
public function notify() // Required
{
foreach ($this->attached as $each) {
$each->update($this);
}
}
public function attach(SplObserver $observer) // Required
{
$this->attached[] = $observer;
}
public function detach(SplObserver $observer) // Required
{
$key = array_keys($this->attached, $observer);
unset($this->attached[$key[0]]);
}
public function setObserverMethod($method, $args = array())
{
$this->observerMethod = $method;
$this->observerArgs = $args;
return $this;
}
public function getObserverMethod()
{
return $this->observerMethod;
}
public function getObserverArgs()
{
return $this->observerArgs;
}
public function getAttached()
{
return $this->attached;
}
}
$db_subj = new DbSubject;
$db_obs = new DbObserver;
$db_subj->attach($db_obs);
$args = array('A');
$db_subj->setObserverMethod('a', $args)->notify();
$args = array('B', 'I can');
$db_subj->setObserverMethod('b', $args)->notify();
$obsvr = new ObserverObserver;
$db_subj->attach($obsvr);
$db_subj->setObserverMethod('c')->notify();
$db_subj->detach($db_obs);
$db_subj->notify();
/**
I run query A
I run query B because I can
I have no argument
I saw that
Nothing happened
**/
You need to make this change:
$arguments = array('apple','banana','pineapple');
$a = new A();
$use_function = array(&$a,"a"); // Make this changes to your code
$instance = new B();
$instance->invoke($use_function,$arguments);
class A {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class B {
function Test() {
invoke(new $A(), "a");
}
function invoke($obj, $method){
// Non static call
call_user_func( array( $obj, $method ) );
// Static call
//call_user_func( array( 'ClassName', 'method' ) );
}
}
I hope this helps.

instantiate a class from a variable in PHP?

I know this question sounds rather vague so I will make it more clear with an example:
$var = 'bar';
$bar = new {$var}Class('var for __construct()'); //$bar = new barClass('var for __construct()');
This is what I want to do. How would you do it? I could off course use eval() like this:
$var = 'bar';
eval('$bar = new '.$var.'Class(\'var for __construct()\');');
But I'd rather stay away from eval(). Is there any way to do this without eval()?
Put the classname into a variable first:
$classname=$var.'Class';
$bar=new $classname("xyz");
This is often the sort of thing you'll see wrapped up in a Factory pattern.
See Namespaces and dynamic language features for further details.
If You Use Namespaces
In my own findings, I think it's good to mention that you (as far as I can tell) must declare the full namespace path of a class.
MyClass.php
namespace com\company\lib;
class MyClass {
}
index.php
namespace com\company\lib;
//Works fine
$i = new MyClass();
$cname = 'MyClass';
//Errors
//$i = new $cname;
//Works fine
$cname = "com\\company\\lib\\".$cname;
$i = new $cname;
How to pass dynamic constructor parameters too
If you want to pass dynamic constructor parameters to the class, you can use this code:
$reflectionClass = new ReflectionClass($className);
$module = $reflectionClass->newInstanceArgs($arrayOfConstructorParameters);
More information on dynamic classes and parameters
PHP >= 5.6
As of PHP 5.6 you can simplify this even more by using Argument Unpacking:
// The "..." is part of the language and indicates an argument array to unpack.
$module = new $className(...$arrayOfConstructorParameters);
Thanks to DisgruntledGoat for pointing that out.
class Test {
public function yo() {
return 'yoes';
}
}
$var = 'Test';
$obj = new $var();
echo $obj->yo(); //yoes
I would recommend the call_user_func() or call_user_func_arrayphp methods.
You can check them out here (call_user_func_array , call_user_func).
example
class Foo {
static public function test() {
print "Hello world!\n";
}
}
call_user_func('Foo::test');//FOO is the class, test is the method both separated by ::
//or
call_user_func(array('Foo', 'test'));//alternatively you can pass the class and method as an array
If you have arguments you are passing to the method , then use the call_user_func_array() function.
example.
class foo {
function bar($arg, $arg2) {
echo __METHOD__, " got $arg and $arg2\n";
}
}
// Call the $foo->bar() method with 2 arguments
call_user_func_array(array("foo", "bar"), array("three", "four"));
//or
//FOO is the class, bar is the method both separated by ::
call_user_func_array("foo::bar"), array("three", "four"));

Categories