I am in a situations where i need to instantiate a class with arguments from within an instance of another class.
Here is the prototype:
//test.php
class test
{
function __construct($a, $b, $c)
{
echo $a . '<br />';
echo $b . '<br />';
echo $c . '<br />';
}
}
Now, i need to instantiate above class using below class's cls function:
class myclass
{
function cls($file_name, $args = array())
{
include $file_name . ".php";
if (isset($args))
{
// this is where the problem might be, i need to pass as many arguments as test class has.
$class_instance = new $file_name($args);
}
else
{
$class_instance = new $file_name();
}
return $class_instance;
}
}
Now when i try to create an instance of test class while passing arguments to it:
$myclass = new myclass;
$test = $myclass->cls('test', array('a1', 'b2', 'c3'));
It gives error:
Missing argument 1 and 2; only first argument is passed.
This works fine if i instantiate a class which has no arguments in it's constructor function.
For experienced PHP developers, above should not be much of a problem. Please help.
Thanks
you need Reflection http://php.net/manual/en/class.reflectionclass.php
if(count($args) == 0)
$obj = new $className;
else {
$r = new ReflectionClass($className);
$obj = $r->newInstanceArgs($args);
}
You can:
1) Modify test class to accept an array, which contains the data you wish to pass.
//test.php
class test
{
function __construct($a)
{
echo $a[0] . '<br />';
echo $a[1] . '<br />';
echo $a[2] . '<br />';
}
}
2) initiate using a user method instead of the constructor and call it using the call_user_func_array() function.
//test.php
class test
{
function __construct()
{
}
public function init($a, $b, $c){
echo $a . '<br />';
echo $b . '<br />';
echo $c . '<br />';
}
}
In your main class:
class myclass
{
function cls($file_name, $args = array())
{
include $file_name . ".php";
if (isset($args))
{
// this is where the problem might be, i need to pass as many arguments as test class has.
$class_instance = new $file_name($args);
call_user_func_array(array($class_instance,'init'), $args);
}
else
{
$class_instance = new $file_name();
}
return $class_instance;
}
}
http://www.php.net/manual/en/function.call-user-func-array.php
Lastly, you can leave your constructor params blank and use func_get_args().
//test.php
class test
{
function __construct()
{
$a = func_get_args();
echo $a[0] . '<br />';
echo $a[1] . '<br />';
echo $a[2] . '<br />';
}
}
http://sg.php.net/manual/en/function.func-get-args.php
You could use call_user_func_array() I believe.
or you could leave the arguments list of the constructor, and then inside the constructor use this
$args = func_get_args();
class textProperty
{
public $start;
public $end;
function textProperty($start, $end)
{
$this->start = $start;
$this->end = $end;
}
}
$object = new textProperty($start, $end);
don't work?
The easiest way I have found:
if ($depCount === 0) {
$instance = new $clazz();
} elseif ($depCount === 1) {
$instance = new $clazz($depInstances[0]);
} elseif ($depCount === 2) {
$instance = new $clazz($depInstances[0], $depInstances[1]);
} elseif ($depCount === 3) {
$instance = new $clazz($depInstances[0], $depInstances[1], $depInstances[2]);
}
Sorry a bit raw, but you should understand the idea.
We're in 2019 now and we have php7 now... and we have the spread-operator (...) .
We can now simply call
<?php
class myclass
{
function cls($file_name, $args = array())
{
include $file_name . ".php";
if (isset($args))
{
$class_instance = new $file_name(...$args); // <-- notice the spread operator
}
else
{
$class_instance = new $file_name();
}
return $class_instance;
}
}
Related
I have this function in my class:
logMagic($mode)
{
# mode
# 1 = all, 2 = dir, 3 = file etc.
# this is wrapped inside a switch statement
# for eases sake here's the case 1: code
$log['dir'] = 'DIRECTORY: '. __DIR__;
$log['file'] = 'FILE: '. __FILE__;
$log['meth'] = 'METHOD: '. __METHOD__;
$log['fnc'] = 'FUNCTION: '. __FUNCTION__;
$log['ns'] = 'NAMESPACE: '. __NAMESPACE__;
$log['cl'] = 'CLASS: '. __CLASS__;
return $log;
}
This is in a foo.php file. I then have a bar.php file where I call and init the class to use this function:
require_once 'foo.php';
$logger = new \Logger('trey.log', 'var/logs');
$logger->logMagic($logger::ALL);
My problem with this is, this will output (in a log file):
DIRECTORY: /var/www/dir
FILE: /var/www/dir/foo.php
METHOD: Logger::logMagic
FUNCTION: logMagic
NAMESPACE:
CLASS: Logger
My expected output was that it would return
DIRECTORY: /var/www/dir
FILE: /var/www/dir/bar.php
METHOD:
FUNCTION:
NAMESPACE:
CLASS:
Reading the docs does clarify this to me that this is normal.
Is there any way I can use magic constants from fileb.php in filea.php, without passing params to the function?
Thanks to the pos dupe link I managed to do some digging to really get what I want. It seems with debug_backtrace() it well.. traces back through each function call. E.g.
fileA.php
class Bar
{
public function foo()
{
echo '<pre>'. print_r(debug_backtrace(), 1) .'</pre>';
return 'hi';
}
}
fileB.php
require_once 'fileA.php';
$bar = new \Bar();
echo $bar->foo();
This outputs:
Array
(
[0] => Array
(
[file] => /var/www/testing/test/fileB.php
[line] => 5
[function] => foo
[class] => Bar
[object] => Bar Object ()
[type] => ->
[args] => Array ()
)
)
hi
This is for the most part, perfect. However, this doesn't gurantee results as the array increases per stack.
E.g. FileC.php calls function in FileB.php which in turn, calls a function in
FileA.php
However, I noted with use of the function that the most desirable one is the end element in the array. With that in mind, I've set up a few functions to mimic functionality of the magic constants, without using any magic.
Set up for use of functions:
$trace = debug_backtrace();
$call = end($trace);
Directory (__DIR__):
# $trace = $call['file']
protected function getDir($trace)
{
$arr = explode('/', $trace);
$file = end($arr);
$directory = [];
$i = 0;
foreach ($arr as $data)
{
if ($data !== $file) {
$directory[] = isset($output) ? $output[$i - 1] . '/' . $data : $data;
$i++;
}
}
return 'DIRECTORY: '. implode('/', $directory);
}
File (__FILE__)::
# $trace = $call['file']
protected function getFile($trace)
{
$arr = explode('/', $trace);
$file = end($arr);
return 'FILE: '. $file;
}
Function/Method (__FUNCTION__ || __METHOD__)::
# $trace = $call
protected function getFunction($trace)
{
$output = 'FUNCTION: '. $trace['function'] ."\n";
foreach ($trace['args'] as $key => $arguments)
{
foreach ($arguments as $k => $arg)
{
if (!is_array($arg)) {
$output .= 'ARGS ('. $k .'): '. $arg ."\n";
}
}
}
return $output;
}
Namespace (__NAMESPACE__):
# $trace = $call['class']
protected function getNamespace($trace)
{
$arr = explode('\\', $trace);
$class = end($arr);
$namespace = [];
$i = 0;
foreach ($arr as $data)
{
if ($data !== $class) {
$namespace[] = isset($output) ? $output[$i - 1] . '/' . $data : $data;
$i++;
}
}
return 'NAMESPACE: '. implode('\\', $namespace);
}
Class (__CLASS__):
# $trace = $call['class']
protected function logClass($trace)
{
if (strpos($trace, '\\') !== false) {
$arr = explode('\\', $trace);
$class = end($arr);
} else {
$class = $trace;
}
$return = 'CLASS: '. $class;
}
Missing Magic Constants:
__LINE__
__TRAIT__
Line is accessible (as you'll see from print_r($call, 1)) but I wasn't in need/interested. Trait is more or less the same as __NAMESPACE__ in my uses, so again, it wasn't interested in creating a function for it.
Notes:
This is part of a class I made that makes use of the protected function via public accessible functions - please ignore :)
These functions could be cleaned up (e.g. instead of $trace = $call['file'], use $file as param)
I'm wondering if this was possible and I could not find a way to do it so I ask. How can I get the name of the variable where in a instance of a class is present.
Pseudo code:
class test{
public $my_var_name = '';
function __construct(){
//the object says: Humm I am wondering what's the variable name I am stored in?
$this->my_var_name = get_varname_of_current_object();
}
}
$instance1 = new test();
$instance2 = new test();
$boeh = new test();
echo $instance1->my_var_name . ' ';
echo $instance2->my_var_name . ' ';
echo $boeh->my_var_name . ' ';
The output would be like:
instance1 instance2 boeh
Why! Well I just wanna know its possible.
I have no idea why, but here you go.
<?php
class Foo {
public function getAssignedVariable() {
$hash = function($object) {
return spl_object_hash($object);
};
$self = $hash($this);
foreach ($GLOBALS as $key => $value) {
if ($value instanceof Foo && $self == $hash($value)) {
return $key;
}
}
}
}
$a = new Foo;
$b = new Foo;
echo '$' . $a->getAssignedVariable(), PHP_EOL; // $a
echo '$' . $b->getAssignedVariable(), PHP_EOL; // $b
I created this code trying to answer for How to get name of a initializer variable inside a class in PHP
But it is already closed and referenced to this question,
just another variant easy to read, and I hope I didn't break any basic concept oh php development:
class Example
{
public function someMethod()
{
$vars = $GLOBALS;
$vname = FALSE;
$ref = &$this;
foreach($vars as $key => $val) {
if( ($val) === ($ref)) {
$vname = $key;
break;
}
}
return $vname;
}
}
$abc= new Example;
$def= new Example;
echo $abc->someMethod();
echo $def->someMethod();
I can't find a good reason to do that.
Anyways, one way you can do (but again it has no use as far as i can imagine) this is by passing the instance name as a constructor's parameter, like this:
$my_instance = new test("my_instance");
I couldn't find a answer to my question and I am thinking there is something easy I am missing..
I am trying to reference a a value within a object with a variable in a class. In this case I want the line on the bottom:
echo $b->ref->$a->type
to output 'testing' like the following two will:
echo $b->ref->test; // outputs 'testing'
$c = $a->type;
echo $b->ref->$c; // outputs 'testing'
Full code:
<?php
class A {
public $type;
public function set_type($type) {
$this->type = $type;
}
}
class B {
public $ref;
public function set_reference($ref) {
$this->ref = $ref;
}
}
$a = new A();
$b = new B();
$b->set_reference( (object) array('test' => 'testing', 'test2' => 'testing2') );
$a->set_type('test');
echo $b->ref->test; // outputs 'testing'
echo '<br />';
echo $a->type; // outputs 'test'
echo '<br />';
$c = $a->type;
echo $b->ref->$c; // outputs 'testing'
echo '<br />';
echo $b->ref->$a->type; // error
What am I missing to be able to do this? Or, is this not possible?
Same as always.
echo $b->ref->{$a->type};
Did you tried this:
echo $b->ref->{$c};
function backtrace() {
var_dump(debug_backtrace());
}
function echosth($what = 'default text') {
echo $what;
backtrace();
}
echosth('another text'); //argument is shown
// ["args"]=> array(1) {[0]=>&string(12) "another text"}
echosth(); //it appears as function has none arguments ["args"]=>array(0) {}
Is there any way to get to default value of parent function ?
Yes, through the PHP reflection class's getDefaultValue.
function foo($test, $bar = 'baz')
{
echo $test . $bar;
}
$function = new ReflectionFunction('foo');
foreach ($function->getParameters() as $param) {
echo 'Name: ' . $param->getName() . PHP_EOL;
if ($param->isOptional()) {
echo 'Default value: ' . $param->getDefaultValue() . PHP_EOL;
}
echo PHP_EOL;
}
Since you know about debug_backtrace, you should be able to get the name of the calling function and run it through that loop.
Using Reflection
http://php.net/manual/en/reflectionparameter.getdefaultvalue.php
I've been trying to think of a way to dynamically return property values for a class using __call instead of creating a slew of functions whose only purpose would be to return those values. The idea I have is to be able to request ( for example ) $this->fetch_PersonFirstName() and have the class check if $this->person["first_name"] is set and return the value. Another example would be calling $this->fetch_BookGenreTitle() and have the class return the value of $this->book["genre"]["title"]. I know something like this would have to do a bit of checking in order for it to automatically determine that, for example, since $this->book["genre_title"] doesn't exist, then it should check for $this->book["genre"]["title"].
So far I've come up with code that ( for some reason ) works for returning the values of an array ( such as my person example ) but my problem quickly develops when I try to return the values of a multidimensional array ( such as with my above book example ). Bare in mind, I'm still trying to think of a way for the __call method to check for the existence of one, and if it doesn't exist, then the other.
Please, throw me a line here. I've been banging my head against the wall trying to figure this out and it's killing me.
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __call($method, $args)
{
$args = implode(", ", $args);
if (preg_match("/^fetch_([A-Z]{1,}[a-z]{1,})(.*)?/", $method, $match))
{
print_r($match);
echo "<br><br>";
$property = strtolower($match[1]);
$indexes = $match[2];
if (property_exists("Test", $property))
{
if ($indexes)
{
$indexes = preg_split("/(?<=[a-z])(?=[A-Z])/", $indexes);
$num_indexes = count($indexes);
$count_indexes = 1;
for ($count=0; $count<$num_indexes; $count++)
{
$record = strtolower($indexes[$count]);
$index .= $record;
$array .= "{$record}";
$var_index = $index;
$var_array = $array;
echo $var_index." {$count}<br>";
echo $var_array." {$count}<br>";
//print_r($this->{$property}{$var_array});
if ($count_indexes == $num_indexes)
{
if (isset($this->{$property}{$var_index}))
{
return $this->{$property}{$var_index};
}
else
{
return $this->{$property}{$var_array};
}
}
else
{
$index .= "_";
}
$count_indexes++;
}
echo "<br><br>";
}
else
{
return $this->{$property};
}
}
}
}
}
?>
<?php
$test = new Test();
echo $test->fetch_PersonFirstName();
echo "<br><br>";
echo $test->fetch_BookGenreTitle();
?>
Thanks again folks. I think I finally have my solution, which works for multidimensional arrays or any depth and accounts for determining whether a property is, for example $this->book["genre_title"] or $this->book["genre"]["title"] :)
I'm posting the code below as someone else may randomly find it useful in the future
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __get($var)
{
if (preg_match_all("/([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)/", $var, $matches))
{
$matches = $matches[1];
$property = strtolower($matches[0]);
if (property_exists($this, $property))
{
unset($matches[0]);
$matches = array_values($matches);
$num_matches = count($matches);
$var = &$this->{$property};
if (!$num_matches)
{
return $var;
}
else
{
foreach($matches as &$match)
{
$match = strtolower($match);
$index .= $match;
if ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
unset($index);
}
elseif ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
}
else
{
$index .= "_";
}
}
return $var;
}
}
}
}
public function iterateArray($var, $index)
{
if (array_key_exists($index, $var))
{
return $var[$index];
}
else
{
return false;
}
}
}
?>
<?php
$test = new Test();
echo $test->PersonFirstName;
echo "<br><br>";
echo $test->BookGenreTitle;
?>
There's more than likely some ways to improve/streamline the code, in which case anyone wanting to do so is more than welcome to post an improved version.
Given "BookGenreTitle" :
Use some sort of regex to separate "Book", "Genre", and "Title"
property_exists($this, "Book")
array_key_exists("genre", $this->book)
If key exists, return $this->book["genre"]
If key doesn't exist, array_key_exists("genre_title", $this->book)
If key exists, return $this->book["genre_title"]
If key doesn't exist, array_key_exists("genre", $this->book) && array_key_exists("title", $this->book["genre"])
Keep going
There's probably a way to use a loop or some sort of recursion instead of hard-coding the maximum depth, but I won't get into that now...
Oh, and as the other poster said, what you want is property overloading (__get and __set).
You need to take a look at property overloading, not method overloading as you've already figured out in the question's title yourself.