PHP empty() on __get accessor - php

Using PHP 5.3 I'm experiencing weird / non-intuitive behavior when applying empty() to dynamic object properties fetched via the __get() overload function. Consider the following code snippet:
<?php
class Test {
protected $_data= array(
'id'=> 23,
'name'=> 'my string'
);
function __get($k) {
return $this->_data[$k];
}
}
$test= new Test();
var_dump("Accessing directly:");
var_dump($test->name);
var_dump($test->id);
var_dump(empty($test->name));
var_dump(empty($test->id));
var_dump("Accessing after variable assignment:");
$name= $test->name;
$id= $test->id;
var_dump($name);
var_dump($id);
var_dump(empty($name));
var_dump(empty($id));
?>
The output of this function is as follow. Compare the results of the empty() checks on the first and second result sets:
Set #1, unexpected result:
string(19) "Accessing directly:"
string(9) "my string"
int(23)
bool(true)
bool(true)
Expecting Set #1 to return the same as Set #2:
string(36) "Accessing after variable assignment:"
string(9) "my string"
int(23)
bool(false)
bool(false)
This is really baffling and non-intuitive. The object properties output non-empty strings but empty() considers them to be empty strings. What's going on here?

Based on a reading of the empty's manual page and comments (Ctrl-F for isset and/or double underscores), it looks like this is known behavior, and if you want your __set and __get methods and empty to play nice together, there's an implicit assumption that you implement a __isset magic method as well.
It is a little bit unintuitive and confusing, but that tends to happen with most meta-programming, particularly in a system like PHP.

In this example, empty() calls the __isset() overload function, not the __get() overload function. Try this:
class Test {
protected $_data= array(
'id'=> 23,
'name'=> 'my string'
);
function __get($k) {
return $this->_data[$k];
}
function __isset($k) {
return isset($this->_data[$k]);
}
}

Related

PHPUnit willReturnMap always returns null

I have a unit test which uses the following mocked class
$this->stubHandle = $this->getMockBuilder(Handle::class)->disableOriginalConstructor()->getMock();
When I run
$key = new Key($this->stubHandle);
$this->stubHandle->method('__call')->will($this->returnCallback('var_dump'));
$key->nonExistingMethod('element0', 'element1', []);
it outputs
string(17) "nonExistingMethod"
and
array(3) {
[0] =>
string(8) "element0"
[1] =>
string(8) "element1"
[2] =>
array(0) {
}
}
which I think it means the first argument of method __call is a string and the second argument is an array. No more arguments passed.
When I mock the return value of method __call like
$this->stubHandle->method('__call')->willReturn(1);
The return value is always 1 as expected.
When I mock the return value of method __call like
$this->stubHandle->method('__call')->willReturnMap(
[
['argument1', null, 'returnValue'],
]
);
I expect the return value to be 'returnValue' when the first argument of method __call is 'argument1' while the seconds argument doesn't care.
If the first argument isn't 'argument1', I expect a return value of null.
Are these expectations correct?
No matter what I try, the return value is always null when I mock this value with method willReturnMap.
If my expectations are wrong, please lead me to a way to accomplish my goals to mock the return value, depending on the first argument (only) and also explain to me how to apply willReturnMap.
It seems my search for method willReturnMap mislead me a bit.
I was under the assumption using null in the map, it would act as a "don't care", but it literally means null.
$this->stubHandle->method('__call')->willReturnMap(
[
['nonExistingMethod', null, 'returnValue'],
]
);
means the arguments of method __call must explicitly be 'nonExistingMethod' & null if you want the return value to be 'returnValue'. Any other argument-values will return null instead.
E.g. $key->nonExistingMethod(null);
For the map to return 'returnValue' in my example, it should have been:
$this->stubHandle->method('__call')->willReturnMap(
[
['nonExistingMethod', ['element0', 'element1', []], 'returnValue'],
]
);
$key->nonExistingMethod('element0', 'element1', []);
In short...
When using argument mapping, the values can't be a "don't Care".

Reassignment of a variable referencing an array reassigns the original variable

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.

PHP - Access value from previously-defined key during array initialization

I'm looking to see if it's possible to access the value of a key I previously defined within the same array.
Something like:
$test = array(
'foo' => 1,
'bar' => $test['foo']
);
I know I can always do so after initialization, I am just wondering if it's possible during initialization?
No, $test doesn't exist until the full constructor is evaluated.
For example: http://codepad.viper-7.com/naUprJ
Notice: Undefined variable: test..
array(2) { ["foo"]=> int(1) ["bar"]=> NULL }
It's probably for the best. Imagine of this worked:
$test = array('foo' => $test['foo']); // mwahaha
If you need to do this a lot, you could create a class that takes keys of a particular format that flag to the class constructor that it should be parsed until all relevant keys are evaluated.

PHP variable length arguments?

In Python and others, there's special syntax for variable length argument lists:
def do_something(*args):
# do something
do_something(1, 2, 3, 4, 5, ...) # arbitrarily long list
I was reading the PHP manual, and it said this:
PHP 4 and above has support for
variable-length argument lists in
user-defined functions. This is really
quite easy, using the func_num_args(),
func_get_arg(), and func_get_args()
functions.
No special syntax is required, and
argument lists may still be explicitly
provided with function definitions and
will behave as normal.
I get the first part. You can pass as many arguments as you'd like to a function that takes no arguments, then get them as an array using func_get_args(), etc. I don't really get what the second part is saying, though.
So, my question is, is there some special syntax for variable length arguments, or some best practice that I don't know about? The approach that the manual suggests seems kludgey at best and makes your function seem like it's taking no arguments (unless I'm doing it wrong). Should I not be trying to use this language feature at all?
Here is a more realistic example:
function Average()
{
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument)
{
$result += $argument;
}
return ($result / max(1, func_num_args()));
}
Average(1, 2, 3, 4, 5); // 3
This is called a variadic function.
Unlike Python's * operator or C#'s params keyword, in PHP you don't even have to specify the variable length arguments. As the second part starts off, "No special syntax is required."
As to the rest of the second paragraph: if you want to specify any required or unrelated arguments that come before the variable-length arguments, specify them in your function signature so your function can handle those. Then to get the variable-length arguments, remove the required variables from func_get_args(), like so:
function func($required) {
// Contains all arguments that come after $required
// as they were present at call time
$args = array_slice(func_get_args(), 1);
}
You don't have to do this (you can still slice from func_get_args() and use its different elements accordingly), but it does make your code more self-documenting.
Since PHP 5.6, a variable argument list can be specified with the ... operator.
function do_something($first, ...$all_the_others)
{
var_dump($first);
var_dump($all_the_others);
}
do_something('this goes in first', 2, 3, 4, 5);
#> string(18) "this goes in first"
#>
#> array(4) {
#> [0]=>
#> int(2)
#> [1]=>
#> int(3)
#> [2]=>
#> int(4)
#> [3]=>
#> int(5)
#> }
As you can see, the ... operator collects the variable list of arguments in an array.
If you need to pass the variable arguments to another function, the ... can still help you.
function do_something($first, ...$all_the_others)
{
do_something_else($first, ...$all_the_others);
// Which is translated to:
// do_something_else('this goes in first', 2, 3, 4, 5);
}
Since PHP 7, the variable list of arguments can be forced to be all of the same type too.
function do_something($first, int ...$all_the_others) { /**/ }
There's no special syntax for variable length argument functions.
Simply use the func_num_args() and func_get_args() functions to get the arguments.
Example:
function callMe(){
if(func_num_args() == 0){
echo 'No arguments =)';
}else{
var_dump(func_get_args());
}
}
The second portion is basically saying that once you start using the variable argument functions, then
function average() { ... }
function average(arg1, arg2, arg3) { ... }
work identically, just that the second version has 3 arguments explicity listed. That's all, don't try to read more into a man page than there is.
I usually do this to avoid kept changing function, and avoid error on argument order
function any_function($ops=array())
{
}
$ops = array('name'=>1, 'type'=>'simplexml', 'callback'=>...);

Get Instance ID of an Object in PHP

I've learn a while ago on StackOverflow that we can get the "instance ID" of any resource, for instance:
var_dump(intval(curl_init())); // int(2)
var_dump(intval(finfo_open())); // int(3)
var_dump(intval(curl_init())); // int(4)
var_dump(intval(finfo_open())); // int(5)
var_dump(intval(curl_init())); // int(6)
I need something similar but applied to classes:
class foo {
public function __construct() {
ob_start();
var_dump($this); // object(foo)#INSTANCE_ID (0) { }
echo preg_replace('~.+#(\d+).+~s', '$1', ob_get_clean());
}
}
$foo = new foo(); // 1
$foo2 = new foo(); // 2
The above works but I was hoping for a faster solution or, at least, one that didn't involve output buffers. Please note that this won't necessarily be used within the constructor or even inside the class itself!
spl_object_hash() is not what I'm looking for because the two objects produce identical hashes
The question previously contained an incorrect example of spl_object_hash output; ensuring that both objects exist at the same time produces hashes which are subtly different:
var_dump(spl_object_hash($foo)); // 0000000079e5f3b60000000042b31773
var_dump(spl_object_hash($foo2)); // 0000000079e5f3b50000000042b31773
Casting to int like resources doesn't seem to work for objects:
Notice: Object of class foo could not be converted to int.
Is there a quick way to grab the same output without using object properties?
Besides var_dump(), I've discovered by trial and error that debug_zval_dump() also outputs the object instance, unfortunately it also needs output buffering since it doesn't return the result.
spl_object_hash() could help you out here. It
returns a unique identifier for the object
which is always the same for a given instance.
EDIT after OP comment:
You could implement such a behavior using a static class property, e.g:
class MyClass
{
private static $_initialized = false;
public function __construct()
{
if (!self::$_initialized) {
self::$_initialized = true;
// your run-only-once code
}
}
}
But actually this has nothing to with your original question.
Well, yes, with an extension.
Note that the handles used for objects that were, in the meantime, destroyed, can be reused.
Build with phpize && ./configure && make && make install
testext.h
#ifndef PHP_EXTTEST_H
# define PHP_EXTTEST_H
# ifdef HAVE_CONFIG_H
# include<config.h>
# endif
# include <php.h>
extern zend_module_entry testext_module_entry;
#define phpext_testext_ptr &testext_module_entry
#endif
testext.c
#include "testext.h"
PHP_FUNCTION(get_object_id)
{
zval *obj;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj)
== FAILURE) {
return;
}
RETURN_LONG(Z_OBJ_HANDLE_P(obj));
}
static zend_function_entry ext_functions[] = {
PHP_FE(get_object_id, NULL)
{NULL, NULL, NULL, 0, 0}
};
zend_module_entry testext_module_entry = {
STANDARD_MODULE_HEADER,
"testext",
ext_functions, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(testext)
config.m4
PHP_ARG_ENABLE(testext,
[Whether to enable the "testext" extension],
[ enable-testext Enable "testext" extension support])
if test $PHP_EXTTEST != "no"; then
PHP_SUBST(EXTTEST_SHARED_LIBADD)
PHP_NEW_EXTENSION(testext, testext.c, $ext_shared)
fi
Test script
<?php
$a = new stdclass();
$b = new stdclass();
var_dump(get_object_id($a));
var_dump(get_object_id($b));
Output
int(1)
int(2)
Alix, your solution in the question was exactly what I needed, but actually breaks when there's an object in an object, returns the last # in the var_dump. I fixed this, made the regex faster, and put it in a nice little function.
/**
* Get global object ID
* From: http://stackoverflow.com/questions/2872366/get-instance-id-of-an-object-in-php
* By: Alix Axel, non-greedy fix by Nate Ferrero
*/
function get_object_id(&$obj) {
if(!is_object($obj))
return false;
ob_start();
var_dump($obj);// object(foo)#INSTANCE_ID (0) { }
preg_match('~^.+?#(\d+)~s', ob_get_clean(), $oid);
return $oid[1];
}
Have a look at spl_object_hash(). Usage example:
$id = spl_object_hash($object);
Note that you'll need PHP 5 >= 5.2.0 for that to work.
As long as you implement the base class all the classes you're going to need this from, you can do something like this:
class MyBase
{
protected static $instances = 0;
private $_instanceId = null;
public function getInstanceId()
{
return $this->_instanceId;
}
public function __construct()
{
$this->_instanceId = ++self::$instances;
}
}
class MyTest extends MyBase
{
public function Foo()
{
/* do something really nifty */
}
}
$a = new MyBase();
$b = new MyBase();
$c = new MyTest();
$d = new MyTest();
printf("%d (should be 1) \n", $a->getInstanceId());
printf("%d (should be 2) \n", $b->getInstanceId());
printf("%d (should be 3) \n", $c->getInstanceId());
printf("%d (should be 4) \n", $d->getInstanceId());
The output would be:
1 (should be 1)
2 (should be 2)
3 (should be 3)
4 (should be 4)
As of PHP 7.2 you can use spl_object_id
$id = spl_object_id($object);
$storage[$id] = $object;
What you're trying to do, is actually Aspect-Oriented Programming (AOP).
There are at least a couple of frameworks available for AOP in PHP at this point:
seasar (formerly PHPaspect) is a larger framework integrating with Eclipse - the screenshot shows you a little code snippet that answers your question, weaving some code around a particular new statement throughout a project.
php-aop is a lightweight framework for AOP.
typo3 has an AOP framework built in.
This may be overkill for your needs, but you may find that exploring the kind of thinking behind ideas like these will lead you down the rabbit hole, and teach you new ways to think about software development in general - AOP is a powerful concept, allowing you to program in terms of strategies and concerns, or "aspects".
Languages like PHP were designed to solve programming tasks - the concept of AOP was designed to solve a programmers' task. When normally you would need to think about how to ensure that a particular concern gets fulfilled every time in your code base, you can think of this as simply an "aspect" of how you're programming, implement it in those terms directly, and count on your concerns to be implemented every time.
It requires less discipline, and you can focus on solving the practical programming tasks rather than trying to architect your way through high-level structural code requirements.
I don't have the PECL runkit enabled to test this, but this may allow you to remove the constructor code from the class definition after the first time that an instance of the class has been created.
Whether you can remove the constructor from within the constructor would be an interesting experiment.
This is a bit late to the party but I didn't see this answer and just recently implemented something similar for a debugging class ( to handle circular references). As you guys may or may not know the normal printing functions such as var_export, have limited or no circular reference support.
As noted the spl_object_hash is unique per instance, the problem I had with it is that it is ugly. Not really suited to printing for my debugger as it's something like this 000000006ac56bae0000000044fda36f which can be hard to compare to say this 000000006ac56bae0000000044fda35f. So like the OP stated what I wanted was just a number of the instance ( I only really needed this on a per class basis ).
Therefor the simple solution for me was to do the following.
$class = get_class( $input );
$hash = spl_object_hash( $input );
if( !isset( $objInstances[ $class ] )){
$objInstances[ $class ] = array();
}
$output = 'object(%s) #%s (%s){%s}'; //class, instance, prop_count, props
if( false === ( $index = array_search($hash, $objInstances[ $class ] ) ) ){
$index = count($objInstances[ $class ]); //set init index for instance
$objInstances[ $class ][] = $hash;
// .... debugging code
$output = 'debugging result.', //sprintf
}else{
$output = sprintf( $output, $class, $index, 0, '#_CIRCULAR_REFRENCE_#');
}
Obviously the debugging code is way more complex, but the essential thing here is that by tracking the class and spl hash in $objInstances I can easily assign my own instance numbers outside of the class. This means I don't need some ugly hack ( that affects the class's code ) to get a reference number. Also, I don't need to display the "ugly" spl hash. Anyway my full code for this outputs something like this.
$obj = new TestObj();
$obj1 = new TestObj();
$obj->setProProp($obj1);
$obj1->setProProp($obj); //create a circular reference
object(TestObj) #0 (7){
["SOME_CONST":const] => string(10) 'some_const',
["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
["SOME_STATIC":public static] => string(6) 'static',
["_PRO_STATIC":protected static] => string(10) 'pro_static',
["someProp":public] => string(8) 'someProp',
["_pro_prop":protected] => object(TestObj) #1 (7){
["SOME_CONST":const] => string(10) 'some_const',
["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
["SOME_STATIC":public static] => string(6) 'static',
["_PRO_STATIC":protected static] => string(10) 'pro_static',
["someProp":public] => string(8) 'someProp',
["_pro_prop":protected] => object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#},
["_proProp":protected] => string(7) 'proProp'
},
["_proProp":protected] => string(7) 'proProp'
}
As you can see it's very easy to see where object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} came from now. I wanted to keep this debugging code as close to the native var_dump
which outputs this.
object(TestObj)#7 (3) {
["someProp"]=> string(8) "someProp"
["_pro_prop":protected]=> object(TestObj)#10 (3) {
["someProp"]=> string(8) "someProp"
["_pro_prop":protected]=> *RECURSION*
["_proProp":protected]=> string(7) "proProp"
}
["_proProp":protected]=> string(7) "proProp"
}
The difference here is I needed the return as a string, not output to the browser. I also wanted to be able to show class constants, static properties, and private properties ( with flags to change what the debugger outputs, and the depth limit). And, I wanted a bit more information as to what the circular reference was instead of just *RECURSION* which doesn't tell me anything.
Hope it helps someone in the future.
Here is the full code for my Debug class, you can find this used about line #300
https://github.com/ArtisticPhoenix/Evo/blob/master/Evo/Debug.php
If you don't want to use output buffering... perhaps use var_export instead of var_dump?

Categories