I have this code:
$originalBar = [
'baz' => 18
];
function modify (&$bar) {
$bar['test'] = true;
$bar = [
'data' => 42
];
global $originalBar;
echo 'same ' . ($bar === $originalBar) . PHP_EOL;
}
modify($originalBar);
var_dump($originalBar);
I know that since the function accepts an argument by reference, any passed array will be modified. So I expect to change the original array with:
$bar['test'] = true;
...and set key test of $originalBar to true. However, when I reassign $bar I expect that the variable no longer points to the original array ($originalBar) and any changes onwards don't modify it. Obviously, that's not the case because the output is:
same 1
array(1) {
["data"]=>
int(42)
}
By reassigning $bar, I reassigned $originalBar too. I expected that this would function the same way as it does in JavaScript, which is why I was confused at first.
My question is - is this documented somewhere? I read the Passing by Reference documentation but I didn't find that there.
Edit: If instead of doing this in the modify function:
$bar = [
'data' => 42
];
...I do this:
$arr = [
'data' => 42
];
$bar = &$arr;
...I get my initially expected result:
same
array(2) {
["baz"]=>
int(18)
["test"]=>
bool(true)
}
It's interesting that in this case, $originalBar is not assigned to the value of $arr and keeps its old value instead.
I'd like to correct some misapprehensions in the question, comments, and self-answer.
Passing objects in PHP behave similarly to passing objects in JS. This is not passing by reference, but passing an "object pointer" by value; the difference is subtle but important. When you pass a variable into a function by reference, you can assign any value to that variable inside the function, and it will have that value on the outside of the function as well. Passing an object does not give you this ability - you can't replace it with a different object, or with the value 42 - but it does let you mutate the object - you can call a method or set a property, and the code outside the function will be able to see the result.
The difference between JS and PHP here is that arrays in JS are objects, but in PHP they are not, so they do not have this "mutable object pointer" behaviour when passed by value.
Pass by reference, as implemented in PHP and many other languages, means that any assignment or modification of the variable inside the function is reflected outside the function. There is nothing special about arrays here, the same thing happens with $foo=42; or $foo++;.
JS does not have an equivalent for explicit pass-by-reference, but other languages such as C# and Pascal have the same concept with different syntaxes.
Things get more unusual in PHP when you assign by reference ($foo =& $bar); one way to think of it is that you have one variable, and you're giving it two names.
I prefer to spell what you wrote as $bar = &$newValue as $bar =& $newValue, because the action isn't really "assign something to $bar" but "bind the name $bar as an alias for something". In binding that alias, it discards any previous binding for that name, so it undoes the "pass by reference" nature of the name.
A possible point of confusion is that arrays in PHP are not the same as arrays/objects in JS and instead behave like strings or numbers for pass-by-value purposes.
In PHP, arrays passed by value will be copied on write when dirtied in a function, much like strings or numerical types:
function modify($a) {
global $foo;
var_dump($a === $foo); # => true
$a['hello'] = "world";
var_dump($a === $foo); # => false, we wrote to $a and it was copied.
}
$foo = ["baz" => 42];
modify($foo);
var_dump($foo); # => ["baz" => 42] (the original was unchanged after the function call)
From a JS perspective, we might expect that $a['hello'] = "world"; would reflect on the outer object and not cause a copy to be created:
const modify = a => {
console.log(a === foo); // => true
a.hello = "world";
console.log(a === foo); // => true
};
const foo = {bar: "baz"};
modify(foo);
console.log(foo); // => {"bar": "baz", "hello": "world"}
The pass-by-value behavior in PHP is unsurprising on objects:
class A {
function __construct() {
$this->bar = "hello";
}
}
function modify($a) {
global $foo;
var_dump($a === $foo); # => true
$a->bar = "world";
var_dump($a === $foo); # => true
}
$foo = new A();
modify($foo);
var_dump($foo); /* => object(A)#1 (1) {
["bar"]=>
string(5) "world"
}
*/
In PHP, passing by reference enables mutation of the original array:
function modify(&$a) {
global $foo;
var_dump($a === $foo); # => true
$a['hello'] = "world";
var_dump($a === $foo); # => true
}
$foo = ["baz" => 42];
modify($foo);
print_r($foo); # => ["baz" => 42, "hello" => "world"]
A reference variable can also be reassigned to a new value:
function modify(&$a) {
global $foo;
var_dump($a === $foo); # => true
$a = "world";
var_dump($a === $foo); # => true
}
$foo = ["baz" => 42];
modify($foo);
print_r($foo); # => "world"
Since JS does't support passing by reference, there is no clear parallel between JS and PHP for these behaviors, aside from the usage of the reference operator to support JS/object-like mutations of arrays inside a function.
That's not documented anywhere because it's the expected behavior. I have just misread. The PHP docs say:
You can pass a variable by reference to a function so the function can modify the variable.
I've had the wrong impression because objects are passed by reference by default in JavaScript and there, reassigning the argument variable doesn't change the original variable.
The intention of passing by reference in PHP is to be able to modify the variable, not just the object it points to. Meaning, you can not only change the object, but also reassign the passed variable.
As #ggorlen said in the comments to my question:
The reference operator is really doing two things: 1) exposing the original memory location for reassignment and 2) allowing a complex structure such as an array to be mutated. In JS, the first is never possible and the second is always possible. PHP gives some "flexibility" in these regards where on the one hand, function ($var) is much more restrictive than most langs while function (&$var) is more permissive than most languages, which is not exactly intuitive.
Related
I've encountered this here and there, and I always worked around it, but I've just gotta know.
Is this an array, an object, or ??? (Let's say I got this via var_export($co))
stdClass::__set_state(array(
'name' => 'Acme Anvil Corp.',
))
Most importantly, how can I add a value to it?
Let's say I want to add a value, like $co->owner = 'Wiley Coyote'. That always throws a warning.
How?
What?
The curiosity is just KILLING me :)
[EDIT]
As a clarification, I guess the root of my question would be "How do I add variables to the object without triggering Warnings?"
Here's the warning I always get:
A PHP Error was encountered
Severity: Warning
Message: Attempt to assign property of non-object
And a var_dump($co) yields: (currently done in a loop, if it's pertinent)
object(stdClass)#185 (1) {
["name"]=>
string(16) "Acme Anvil Corp."
}
[/EDIT]
$co is object of type stdClass. You can create one and add as many properties as you want without any warning:
$obj = new stdClass();
$obj->prop1 = 'val1';
$obj->prop2 = 3.14;
The warning you are facing is most probably Creating default object from null value, meaning that you are trying to assign property to uninitialized variable.
You should not try running the code produced by var_export for stdClass objects, since they don't have method __setState.
Not too sure about your error - but the stdClass class is a default in built class to PHP.
It's basically an empty object
You should be able to set properties with $class->foo = 'bar'
If you're not talking about stdClass - but the fact you're getting an array in var_export - it's simply a representation of the internal object state .
class Foo
{
private $bar = 'baz';
}
$foo = new Foo();
var_export($foo);
Should give output similar to
Foo::__set_state(array(
'bar' => 'baz',
))
The warning you get may be due to the property being protected/private.
var_dump() generally tells you about the access modifiers on internal objects better than var_export()
Can you clarify what the warning you get is and what the question is regarding, and I'll edit up my answer to make it relevant to what you're asking
var_export() prints executable PHP code able to recreate the variable exported.
When encountering objects, they are always exported as a call to their static magic method __set_state(). Compared to other magic methods, this method does not really have much magic in it besides the fact that var_export guarantees to create static calls with this method, so it is simply just the agreed upon name for such a method.
The __set_state() method of a class should return an instance of the object with all exported properties set to the values given as the array parameter.
Example:
class Foo {
public $bar;
public static function __set_state($params) {
$obj = new Foo;
foreach ($params as $key => $value) {
$obj->$key = $value;
}
return $obj;
}
}
$foo = new Foo;
$foo->bar = "Hello World";
var_export($foo);
This prints something like:
Foo::__set_state(array('bar' => 'Hello World'));
If you execute this and save it to a variable again:
$x = Foo::__set_state(array('bar' => 'Hello World'));
it will recreate the object with all given properties set.
It does not work with stdClass objects, because they don't have a __set_state() method.
See http://de3.php.net/manual/en/language.oop5.magic.php#object.set-state for more info.
Update:
This code does not trigger any warnings:
ini_set('display_errors', 1);
error_reporting(E_ALL | E_STRICT);
$co = new stdClass();
$co->name = 'Acme Anvil Corp.';
var_export($co);
$co->owner = 'Wiley Coyote';
Can pass a value to specific argument in function ?
function fun1($a,$b)
{
echo $b;
}
#fun1(123);
Functions can be defined so that they do not require all parameters. For example:
function foo($a, $b = 2) {
echo $a + $b;
}
foo(1); //gives 3
Read about default function values here
However, you cannot pass in later parameters without specifying earlier ones. Some simple programming-function-parameters-basics... when you do foo($b) the function has no idea that the variable was named b... It just gets the data; usually a primitive type (in this case an int) or a reference.
You haven't specified how you're using these variables, so you may want to give a dummy value like "-1" to $a (and handle it in your function) (foo(-1, 123)), or rewrite your function so that $a is the second parameter with the default value. (function foo($b, $a = NULL))
That's why you must pass the variables in order; the function assumes you're using it right, and it lines up the values passed with the function definition. function foo($a, $b) means "I'm assuming I should associate your first value with a and your second value with b)".
With your original example function foo($a, $b):
No context, so I would just say do this function foo($b, $a = some_default_value). However, I'm assuming you're using $a and $b equally so you could check to see if it was some default-invalid-value and act on it. However, if your function performs different tasks depending on the (number of) parameters passed, you probably want to separate your function.
If you insist on not switching the order, you could call foo(-1, 123) with a dummy value and check it. Again though, same problem as above
Edit: You've given another example foo($a, $b, $c) and you said you want to do foo($b) to update the middle value. See the explanation in the first paragraph about how a function knows what parameter is what.
If you mean you want to pass an arbitrary set of variables to a function and it knows which ones it got? Again I don't think this is the best practice (you'll need to give us more detail about how you're using this) but you could pass an associative array:
function foo($arr) {
if (isset($arr['a'])) {
echo $a;
}
if (isset($arr['b'])) {
echo $b;
}
if (isset($arr['c'])) {
echo $c;
}
}
foo(array('b' => 123));
I feel horrible after writing this function :P
<?php
function FUN1($a, $b)
{
echo "HI";
echo $b;
} //$_a= 123; //error_reporting (E_ALL ^ E_NOTICE ^ E_WARNING); //$b=23; echo #FUN1(123);//it gives HI123
?>
I formatted your function. Firstly, when I tried that call it doesn't give me "HI123". Secondly, # is bad practice and really slows down the code. Thirdly, you don't echo FUN1 since it doesn't return anything; your function prints the stuff itself.
You (your student) are/is going in the wrong direction. As I said in my comment, functions already have a beautiful way of sorting out the parameters. Instead of trying to do something funky and work around that, just change your approach.
The example above has no real use and I'm sure in actual code you should just write different functions when you're setting different variables. like setA($a) setB($b) setC($c) setAll($a, $b, $c) and use them accordingly. Arrays are useful for easy variable length functions, but if you're checking each tag to do something, then something's wrong.
If you only want to pass one argument, you could make a wrapper function like this:
function PassOne($arg)
{
fun1(NULL,$arg);
}
function fun1($a,$b)
{
echo $b;
}
Forgive any inaccuracies. It's been a while since I coded in PHP.
If you want to ensure the order of arguments, you can pass a single array as an argument.
$args = array(
'name' => 'Robert',
'ID' => 12345,
'isAdmin' => true
);
example($args);
function example($args)
{
echo $args['name']; // prints Robert
echo $args['ID']; // prints 12345
echo $args['isAdmin']; // prints true
}
Using this approach, you can also hard-code default values into the function, replacing them only when they're provided in the argument array. Example:
$args = array(
'name' => 'Robert',
'ID' => 12345
// Note that I didn't specify whether Robert was admin or not
);
example($args);
function example($args)
{
$defaultArgs = array(
'name' => '',
'ID' => -1,
'isAdmin' => false // provides a default value to incomplete requests
);
// Create a new, mutable array that's a copy of the default arguments
$mixArgs = $defaultArgs;
// replace the default arguments with what was provided
foreach($args as $k => $v) {
$mixArgs[$k] = $v;
}
/*
Now that we have put all the arguments we received into $mixArgs,
$mixArgs is mix of supplied values and default values. We can use
this fact to our advantage:
*/
echo $mixArgs['name']; // prints Robert
// if ID is still set to the default value, the user never passed an ID
if ($mixArgs['ID'] == -1) {
die('Critical error! No ID supplied!'); // use your imagination
} else {
echo mixArgs['ID']; // prints 12345
}
echo mixArgs['isAdmin']; // prints false
// ... etc. etc.
}
2018's PHP syntax and defaults
function example($args=[], $dftArgs=['name'=>'', 'ID' => -1, 'isAdmin'=>false])
{
if (is_string($args))
$args = json_decode($args,true); // for microservice interoperability
$args = array_merge($dftArgs,$args);
// ... use $args
}
// PS: $dftArgs as argument is not usual, is only a generalization
No.
But by convention you can skip arguments to built in functions by passing NULL in that position:
fun1(NULL, 123);
Obviously this is doesn't make sense for everything - for example this makes no sense:
$result = strpos(NULL, 'a string');
For user defined functions, it's up to you to handle the arguments in whatever way you see fit - but you might find func_get_arg()/func_get_args() useful for functions that use an indeterminate number of arguments.
Also, don't forget you can make arguments optional by defining default values:
function fun ($arg = 1) {
echo $arg;
}
fun(2); // 2
fun(); // 1
Note that default values can only be defined on the right-most arguments. You cannot give an argument a default value if an argument to its right does not have one. So this is illegal:
function fun ($arg1 = 1, $arg2) {
// Do stuff heere
}
In the following example, is it possible to access the 'str' value from within the anonymous function?
$foo = array(
'str' => 'THIS IS A STRING',
'fn' => function () {
// is it possible from within here to access 'str'?
}
);
if $foo is defined in the global namespace you should be able to access it via $GLOBALS['foo']['str'] (or make it available via the global $foo; construct). If it isn't (local var, parameter, member variable, …), you have to pass it (as reference!) to the anonymous function:
$foo = array(
'str' => 'THIS IS A STRING',
'fn' => function () use(&$foo) {
echo $foo['str'];
}
);
I have found a different way to do this without using global variables:
<?php
$arr = array("a" => 12,
"b" => function($num)
{
$c = $num * 2;
return $c;
});
echo $arr["b"]($arr["a"]);
?>
Note the weird syntax of ending the array index call with method parentheses. By passing $arr["a"] as a parameter you can have access to that value (I imagine you could pass by reference as well).If you were not to pass anything into the anonymous function, you would call it like this:
$arr["b"]();
If you don't include the method parentheses it won't work.
with a new array I do this:
$aVal = array();
$aVal[key1][var1] = "something";
$aVal[key1][var2] = "something else";
Is there a similar syntax for an object
(object)$oVal = "";
$oVal->key1->var1 = "something";
$oVal->key1->var2 = "something else";
$x = new stdClass();
A comment in the manual sums it up best:
stdClass is the default PHP object.
stdClass has no properties, methods or
parent. It does not support magic
methods, and implements no interfaces.
When you cast a scalar or array as
Object, you get an instance of
stdClass. You can use stdClass
whenever you need a generic object
instance.
The standard way to create an "empty" object is:
$oVal = new stdClass();
But I personally prefer to use:
$oVal = (object)[];
It's shorter and I personally consider it clearer because stdClass could be misleading to novice programmers (i.e. "Hey, I want an object, not a class!"...).
(object)[] is equivalent to new stdClass().
See the PHP manual (here):
stdClass: Created by typecasting to object.
and here:
If an object is converted to an object, it is not modified. If a value of any other type is converted to an object, a new instance of the stdClass built-in class is created.
and here (starting with PHP 7.3.0, var_export() exports an object casting an array with (object)):
Now exports stdClass objects as an array cast to an object ((object) array( ... )), rather than using the nonexistent method stdClass::__setState(). The practical effect is that now stdClass is exportable, and the resulting code will even work on earlier versions of PHP.
However remember that empty($oVal) returns false, as #PaulP said:
Objects with no properties are no longer considered empty.
Regarding your example, if you write:
$oVal = new stdClass();
$oVal->key1->var1 = "something"; // this creates a warning with PHP < 8
// and a fatal error with PHP >=8
$oVal->key1->var2 = "something else";
PHP < 8 creates the following Warning, implicitly creating the property key1 (an object itself)
Warning: Creating default object from empty value
PHP >= 8 creates the following Error:
Fatal error: Uncaught Error: Undefined constant "key1"
In my opinion your best option is:
$oVal = (object)[
'key1' => (object)[
'var1' => "something",
'var2' => "something else",
],
];
I want to point out that in PHP there is no such thing like empty object in sense:
$obj = new stdClass();
var_dump(empty($obj)); // bool(false)
but of course $obj will be empty.
On other hand empty array mean empty in both cases
$arr = array();
var_dump(empty($arr));
Quote from changelog function empty
Objects with no properties are no longer considered empty.
Short answer
$myObj = new stdClass();
// OR
$myObj = (object) [
"foo" => "Foo value",
"bar" => "Bar value"
];
Long answer
I love how easy is to create objects of anonymous type in JavaScript:
//JavaScript
var myObj = {
foo: "Foo value",
bar: "Bar value"
};
console.log(myObj.foo); //Output: Foo value
So I always try to write this kind of objects in PHP like javascript does:
//PHP >= 5.4
$myObj = (object) [
"foo" => "Foo value",
"bar" => "Bar value"
];
//PHP < 5.4
$myObj = (object) array(
"foo" => "Foo value",
"bar" => "Bar value"
);
echo $myObj->foo; //Output: Foo value
But as this is basically an array you can't do things like assign anonymous functions to a property like js does:
//JavaScript
var myObj = {
foo: "Foo value",
bar: function(greeting) {
return greeting + " bar";
}
};
console.log(myObj.bar("Hello")); //Output: Hello bar
//PHP >= 5.4
$myObj = (object) [
"foo" => "Foo value",
"bar" => function($greeting) {
return $greeting . " bar";
}
];
var_dump($myObj->bar("Hello")); //Throw 'undefined function' error
var_dump($myObj->bar); //Output: "object(Closure)"
Well, you can do it, but IMO isn't practical / clean:
$barFunc = $myObj->bar;
echo $barFunc("Hello"); //Output: Hello bar
Also, using this synthax you can find some funny surprises, but works fine for most cases.
php.net said it is best:
$new_empty_object = new stdClass();
In addition to zombat's answer if you keep forgetting stdClass
function object(){
return new stdClass();
}
Now you can do:
$str='';
$array=array();
$object=object();
You can use new stdClass() (which is recommended):
$obj_a = new stdClass();
$obj_a->name = "John";
print_r($obj_a);
// outputs:
// stdClass Object ( [name] => John )
Or you can convert an empty array to an object which produces a new empty instance of the stdClass built-in class:
$obj_b = (object) [];
$obj_b->name = "John";
print_r($obj_b);
// outputs:
// stdClass Object ( [name] => John )
Or you can convert the null value to an object which produces a new empty instance of the stdClass built-in class:
$obj_c = (object) null;
$obj_c->name = "John";
print($obj_c);
// outputs:
// stdClass Object ( [name] => John )
Use a generic object and map key value pairs to it.
$oVal = new stdClass();
$oVal->key = $value
Or cast an array into an object
$aVal = array( 'key'=>'value' );
$oVal = (object) $aVal;
to access data in a stdClass in similar fashion you do
with an asociative array just use the {$var} syntax.
$myObj = new stdClass;
$myObj->Prop1 = "Something";
$myObj->Prop2 = "Something else";
// then to acces it directly
echo $myObj->{'Prop1'};
echo $myObj->{'Prop2'};
// or what you may want
echo $myObj->{$myStringVar};
You can try this way also.
<?php
$obj = json_decode("{}");
var_dump($obj);
?>
Output:
object(stdClass)#1 (0) { }
If you want to create object (like in javascript) with dynamic properties, without receiving a warning of undefined property.
class stdClass {
public function __construct(array $arguments = array()) {
if (!empty($arguments)) {
foreach ($arguments as $property => $argument) {
if(is_numeric($property)):
$this->{$argument} = null;
else:
$this->{$property} = $argument;
endif;
}
}
}
public function __call($method, $arguments) {
$arguments = array_merge(array("stdObject" => $this), $arguments); // Note: method argument 0 will always referred to the main class ($this).
if (isset($this->{$method}) && is_callable($this->{$method})) {
return call_user_func_array($this->{$method}, $arguments);
} else {
throw new Exception("Fatal error: Call to undefined method stdObject::{$method}()");
}
}
public function __get($name){
if(property_exists($this, $name)):
return $this->{$name};
else:
return $this->{$name} = null;
endif;
}
public function __set($name, $value) {
$this->{$name} = $value;
}
}
$obj1 = new stdClass(['property1','property2'=>'value']); //assign default property
echo $obj1->property1;//null
echo $obj1->property2;//value
$obj2 = new stdClass();//without properties set
echo $obj2->property1;//null
As others have pointed out, you can use stdClass. However based on the question, it seems like what you really want is to be able to add properties to an object on the fly. You don't need to use stdClass for that, although you can. Really you can use any class. Just create an object instance of any class and start setting properties. I like to create my own class whose name is simply o with some basic extended functionality that I like to use in these cases and is nice for extending from other classes. Basically it is my own base object class. I also like to have a function simply named o(). Like so:
class o {
// some custom shared magic, constructor, properties, or methods here
}
function o() {
return new o();
}
If you don't like to have your own base object type, you can simply have o() return a new stdClass. One advantage is that o is easier to remember than stdClass and is shorter, regardless of if you use it as a class name, function name, or both. Even if you don't have any code inside your o class, it is still easier to memorize than the awkwardly capitalized and named stdClass (which may invoke the idea of a 'sexually transmitted disease class'). If you do customize the o class, you might find a use for the o() function instead of the constructor syntax. It is a normal function that returns a value, which is less limited than a constructor. For example, a function name can be passed as a string to a function that accepts a callable parameter. A function also supports chaining. So you can do something like:
$result= o($internal_value)->some_operation_or_conversion_on_this_value();
This is a great start for a base "language" to build other language layers upon with the top layer being written in full internal DSLs. This is similar to the lisp style of development, and PHP supports it way better than most people realize. I realize this is a bit of a tangent for the question, but the question touches on what I think is the base for fully utilizing the power of PHP.
EDIT/UPDATE:
I no longer recommend any of this. It makes it hard for static analysis tools and IDEs and custom AST based tools to understand, validate, help you lookup or write your code. Generally magic is bad except in some cases if you are able to get your tools to understand the magic and if it you do it in a standard enough way that even standard community tools will understand it or if your tools are so advanced and full featured that you only use your own tools. Also, I think they are deprecating the ability to add properties to random objects in an upcoming version of PHP, I think it will only work with certain ones, but I don't recommend using that feature anyways.
If you don't want to do this:
$myObj = new stdClass();
$myObj->key_1 = 'Hello';
$myObj->key_2 = 'Dolly';
You can use one of the following:
PHP >=5.4
$myObj = (object) [
'key_1' => 'Hello',
'key_3' => 'Dolly',
];
PHP <5.4
$myObj = (object) array(
'key_1' => 'Hello',
'key_3' => 'Dolly',
);
Here an example with the iteration:
<?php
$colors = (object)[];
$colors->red = "#F00";
$colors->slateblue = "#6A5ACD";
$colors->orange = "#FFA500";
foreach ($colors as $key => $value) : ?>
<p style="background-color:<?= $value ?>">
<?= $key ?> -> <?= $value ?>
</p>
<?php endforeach; ?>
stdClass is the default PHP object. stdClass has no properties, methods or parent. It does not support magic methods, and implements no interfaces.
When you cast a scalar or array as Object, you get an instance of stdClass. You can use stdClass whenever you need a generic object instance.
<?php
// ways of creating stdClass instances
$x = new stdClass;
$y = (object) null; // same as above
$z = (object) 'a'; // creates property 'scalar' = 'a'
$a = (object) array('property1' => 1, 'property2' => 'b');
?>
stdClass is NOT a base class! PHP classes do not automatically inherit from any class. All classes are standalone, unless they explicitly extend another class. PHP differs from many object-oriented languages in this respect.
<?php
// CTest does not derive from stdClass
class CTest {
public $property1;
}
$t = new CTest;
var_dump($t instanceof stdClass); // false
var_dump(is_subclass_of($t, 'stdClass')); // false
echo get_class($t) . "\n"; // 'CTest'
echo get_parent_class($t) . "\n"; // false (no parent)
?>
You cannot define a class named 'stdClass' in your code. That name is already used by the system. You can define a class named 'Object'.
You could define a class that extends stdClass, but you would get no benefit, as stdClass does nothing.
(tested on PHP 5.2.8)
You have this bad but usefull technic:
$var = json_decode(json_encode([]), FALSE);
You can also get an empty object by parsing JSON:
$blankObject= json_decode('{}');
Seeking suggestions from PHP architects!
I'm not terribly familiar with PHP but have taken over maintenance of a large analytics package written in the language. The architecture is designed to read reported data into large key/value arrays, which are passed through various parsing modules to extract those report parameters known to each of those modules. Known parameters are removed from the master array, and any leftovers which were not recognized by any of the modules, are dumped into a kind of catch-all report showing the "unknown" data points.
There are a few different methods being used to call these parser modules, and I would like to know which if any are considered to be "proper" PHP structure. Some are using pass-by-reference, others pass-by-value, some are functions, some are objects. All of them modify the input parameter in some way.
A super-simplified example follows:
#!/usr/bin/php
<?php
$values = Array("a"=>1, "b"=>2, "c"=>3, "d"=>4 );
class ParserA {
private $a = null;
public function __construct(&$myvalues) {
$this->a = $myvalues["a"];
unset($myvalues["a"]);
}
public function toString() { return $this->a; }
}
// pass-by-value
function parse_b($myvalues) {
$b = $myvalues["b"];
unset($myvalues["b"]);
return Array($b, $myvalues);
}
// pass-by-reference
function parse_c(&$myvalues) {
echo "c=".$myvalues["c"]."\n";
unset($myvalues["c"]);
}
// Show beginning state
print_r($values);
// will echo "1" and remove "a" from $values
$a = new ParserA($values);
echo "a=".$a->toString()."\n";
print_r($values);
// w ill echo "2" and remove "b" from $values
list($b, $values) = parse_b($values);
echo "b=".$b."\n";
print_r($values);
// will echo "3" and remove "c" from $values
parse_c($values);
print_r($values);
?>
The output will be:
Array
(
[a] => 1
[b] => 2
[c] => 3
[d] => 4
)
a=1
Array
(
[b] => 2
[c] => 3
[d] => 4
)
b=2
Array
(
[c] => 3
[d] => 4
)
c=3
Array
(
[d] => 4
)
I'm really uncomfortable having so many different call methods in use, some of which have hidden effects on the call function parameters using "&pointer"-style functions, some requiring the main body to write their output, and some writing their output independently.
I would prefer to choose a single methodology and stick with it. In order to do so, I would also like to know which is most efficient; my reading of the PHP documentation indicates that since it uses copy-on-write, there shouldn't be much performance difference between using pointers to vs passing the object directly and re-reading a return value. I would also prefer to use the object-oriented structure, but am uncomfortable with the hidden changes being made to the input parameter on the constructor.
Of the three calling methods, ParserA(), parse_b(), and parse_c(), which if any is the most appropriate style?
I'm not really an expert in PHP but from my experience passing by value is better. This way code won't have side effects and that mean it will be easier to understand and maintain and do all sorts of crazy things on it, like using it as callback for map function. So I'm all for parse_b way of doing things.
FYI: In PHP, objects are always passed by reference, no matter what. Also if you have an array with objects and scalar values in it, the scalar values are passed by value, but the objects by reference.
As a general rule in PHP, do not use references unless you really have to.
references in PHP are also not what most people expect them to be:
"References in PHP are a means to access the same variable content by different names. They are not like C pointers; instead, they are symbol table aliases.""
see also: php.net: What References Are
So in short:
The proper way of handling this PHP is using creating an object that passes the variables around by value or manipulating the array with array_map (array_map allows you to apply a callback function to the elements an array.)
I would vote against the methods proposed in general, but of them, I think parse_b has the best idea.
I think it would be better design to wrap the "data" array in a class that could let you "pop" a key out of it easily. So the parser ends up looking like:
class ParserA {
private $a = null;
public function __construct(My_Data_Class $data) {
$this->a = $data->popValue("a");
}
public function toString() { return $this->a; }
}
And a sample implementation
class My_Data_Class {
protected $_data;
public function __construct(array $data) {
$this->_data = $data;
}
public function popValue($key) {
if (isset($this->_data[$key])) {
$value = $this->_data[$key];
unset($this->_data[$key]);
return $value;
}
}
}