Assignment by reference or value when using $this? - php

I'm confused about the following output in this linked list
class ListNode{
public $next = NULL;
public $data = NULL;
public function __construct($data){
$this->data = $data;
}
}
class LinkedList{
private $firstNode = NULL;
private $lastNode = NULL;
public function insertFirst($data){
$link = new ListNode($data);
$link->next = $this->firstNode;
$this->firstNode = &$link;
if($this->lastNode == NULL){
$this->lastNode = &$link;
}
}
public function readList(){
while($this->firstNode != NULL){
echo $this->firstNode->data;
$this->firstNode = $this->firstNode->next;
}
}
public function assessList(){
$copy = $this->firstNode;
echo $copy->data;
echo $this->firstNode->data;
$copy->data = 'm';
echo $copy->data;
echo $this->firstNode->data;
}
}
$linkedList = new LinkedList();
$linkedList->insertFirst('c');
$linkedList->insertFirst('b');
$linkedList->insertFirst('a');
//$linkedList->readList(); //output a b c
$linkedList->assessList(); //outputs a a m m
I would expect the output to be a a m a. I thought $copy is just a copy of the value stored in $this->firstNode.
Isn't this line of code $copy = $this->firstNode an assignment by value? I would expect the output to be a a m m if it was an assignment by reference $copy = &$this->firstNode but not if it was an assignment by value.
Can someone please clarify?
EDIT (additional example)
public function assessList(){
$copy = $this->firstNode->data;
echo $copy. "<br/>";
echo $this->firstNode->data. "<br/>";
$copy = 'm';
echo $copy. "<br/>";
echo $this->firstNode->data. "<br/>";
}

This:
$copy = $this->firstNode;
Is not a copy of the object, it's a copy of the "pointer" to the original object, so when you modify it, you modify the underlying object. You need to use the clone keyword to get a true copy:
$copy = clone $this->firstNode;
From the PHP docs (emphasis mine):
When assigning an already created instance of a class to a new
variable, the new variable will access the same instance as the object
that was assigned.
You can see from this example that your code snippet now outputs:
aama

Related

PHP arrays not index-able

I am porting C# code to PHP.
However, it seems like PHP ignores the array references. The code below gives out the last element 3 times. How can I fix this?
class Participants
{
public $name;
public $country;
public $town;
}
class Test
{
public function __construct()
{
$this->List = array(new Participants());
}
public function test()
{
$p = new Participants();
$p->name = "Harry";
$p->town = "Washington";
$p->country = "USA";
$List[0] = $p;
$p->name = "Janette";
$p->town = "Amsterdam";
$p->country = "Netherlands";
$List[1] = $p;
$p->name = "Piotr";
$p->town = "Moscow";
$p->country = "Russia";
$List[2] = $p;
echo $List[0]->name;
echo $List[1]->name;
echo $List[2]->name;
// this unfortunately ignores the index and echoes 3 times the last element.
}
}//end Test Class
Arrays in PHP are actually ordered hashmaps and they are indexable. Your problem has nothing to do with "indexes being ignored". Your problem is that $List[0], $List[1] and $List[2] are all references to the same object.
Objects are mutable, you only make 1 unique object and change the name/town/country 3 times.
When you assign $p to $List[0] you do not make a copy of the object, but instead give $List[0] a reference to the same object as $p, which you then proceed to alter.
If you want 3 objects you need to make 3 objects:
$p = new Participants();
$p->name = "Harry";
$p->town = "Washington";
$p->country = "USA";
$List[0] = $p;
$p = new Participants();
$p->name = "Janette";
$p->town = "Amsterdam";
$p->country = "Netherlands";
$List[1] = $p;
$p = new Participants();
$p->name = "Piotr";
$p->town = "Moscow";
$p->country = "Russia";
$List[2] = $p;
This works the same in nearly every OOP language BTW, including C#. The difference is that in C# you can define a value type (struct or enumeration) which does make a full copy when assigning. My guess is that the C# Participants is a struct.

Get variable name of object inside the object php

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");

PHP Object Indices as Strings?

Is there a way to access a member of an object, by using its name as a string?
When I declare an array...
$array = array();
$array['description_en']="hello";
$array['description_fr']="bonjour";
then I access a member like this:
$lang="en"; //just to show my purpose. it will be dynamic
$description = $array['description_'.$lang];
Can I do the same thing for objects?
For example:
$obj->description_en="hello";
$obj->description_fr="bonjour";
How can I access $obj->description_.$lang ?
class test
{
public $description_en = 'english';
}
$obj = new test();
$lang = 'en';
echo $obj->{"description_".$lang}; // echo's "english"
You can see more examples of variable variables here.
You can use this syntax:
<?php
class MyClass {
public $varA = 11;
public $varB = 22;
public $varC = 33;
}
$myObj = new MyClass();
echo $myObj->{"varA"} . "<br>";
echo $myObj->{"varB"} . "<br>";
echo $myObj->{"varC"} . "<br>";
This way, you can access object variables as if they were entries in an associative array.

PHP: How to instantiate a class with arguments from within another class

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;
}
}

In PHP, How to Convert an Argument Name into a String

My goal is to echo the argument passed to a function. For example, how can this be done?
$contact_name = 'foo';
function do_something($some_argument){
// echo 'contact_name' .... How???
}
do_something($contact_name);
You can't. If you want to do that, you need to pass the names as well, e.g:
$contact_name = 'foo';
$contact_phone = '555-1234';
function do_something($args = array()) {
foreach ($args as $name => $value) {
echo "$name: $value<br />";
}
}
do_something(compact('contact_name', 'contact_phone'));
Straight off the PHP.net variables page:
<?php
function vname(&$var, $scope=false, $prefix='unique', $suffix='value')
{
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = $prefix.rand().$suffix;
$vname = FALSE;
foreach($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
?>
Not possible.
Variables are just means to address values or areas in the memory. You cannot get the variable name that’s value has been passed to a function.
Disclaimer: this will oonly work if you pass a variable to the function, not a value, and it only works when your not in a function or a class. So only the GLOBAL scope works :)
Good funct($var)
Bad funct(1)
You can do it actually contrary to popular believe ^_^. but it involves a few lookup tricks with the $GLOBALS variable.
you do it like so:
$variable_name = "some value, better if its unique";
function funct($var) {
foreach ($GLOBALS as $name => $value) {
if ($value == $var) {
echo $name; // will echo variable_name
break;
}
}
}
this method is not fool proof tho. Because if two variables have the same value, the function will get the name of the first one it finds. Not the one you want :P
Its best to make the variable value unique before hand if you want accuracy on variable names
Another method would be to use reference to be accurate like so
$variable_name = 123;
function funct(&$var) {
$old = $var;
$var = $checksum = md5(time()); // give it unique value
foreach ($GLOBALS as $name => $value) {
if ($value == $var) {
echo $name; // will echo variable_name
$var = $old; // reassign old value
break;
}
}
}
so it is entirely possible :)
Based on PTBNL's (most definately correct) answer i came up with a more readable (at least i think so) approach:
/**
* returns the name of the variable posted as the first parameter.
* If not called from global scope, pass in get_defined_vars() as the second parameter
*
* behind the scenes:
*
* this function only works because we are passing the first argument by reference.
* 1. we store the old value in a known variable
* 2. we overwrite the argument with a known randomized hash value
* 3. we loop through the scope's symbol table until we find the known value
* 4. we restore the arguments original value and
* 5. we return the name of the symbol we found in the table
*/
function variable_name( & $var, array $scope = null )
{
if ( $scope == null )
{
$scope = $GLOBALS;
}
$__variable_name_original_value = $var;
$__variable_name_temporary_value = md5( number_format( microtime( true ), 10, '', '' ).rand() );
$var = $__variable_name_temporary_value;
foreach( $scope as $variable => $value )
{
if ( $value == $__variable_name_temporary_value && $variable != '__variable_name_original_value' )
{
$var = $__variable_name_original_value;
return $variable;
}
}
return null;
}
// prove that it works:
$test = 1;
$hello = 1;
$world = 2;
$foo = 100;
$bar = 10;
$awesome = 1;
function test_from_local_scope()
{
$local_test = 1;
$local_hello = 1;
$local_world = 2;
$local_foo = 100;
$local_bar = 10;
$local_awesome = 1;
return variable_name( $local_awesome, get_defined_vars() );
}
printf( "%s\n", variable_name( $awesome, get_defined_vars() ) ); // will echo 'awesome'
printf( "%s\n", test_from_local_scope() ); // will also echo awesome;
Sander has the right answer, but here is the exact thing I was looking for:
$contact_name = 'foo';
function do_something($args = array(), $another_arg) {
foreach ($args as $name => $value) {
echo $name;
echo '<br>'.$another_arg;
}
}
do_something(compact(contact_name),'bar');
class Someone{
protected $name='';
public function __construct($name){
$this->name=$name;
}
public function doSomthing($arg){
echo "My name is: {$this->name} and I do {$arg}";
}
}
//in main
$Me=new Someone('Itay Moav');
$Me->doSomething('test');

Categories