Given the following array:
$array = array(
'item_1' => array(
'item_1_1' => array(
'item_1_1_1' => 'Hello',
),
'item_1_2' => 'World',
),
'item_2' => array(),
);
How can I convert that into an Object?
Option 1
$obj = (object) $array;
Or
Option 2
$object = json_decode(json_encode($array), FALSE);
Or something else?
I would like to know the difference in the output between the 2 option and understand the best practice for creating this conversion.
Well you are answering somehow your own question, but if you want to have an object with the attributes like your array you have to cast it, this way an array will remain an array
$obj = (object) $array;
OUTPUT:
object(stdClass)#1 (2) {
["item_1"]=>
array(2) {
["item_1_1"]=>
array(1) {
["item_1_1_1"]=>
string(5) "Hello"
}
["item_1_2"]=>
string(5) "World"
}
["item_2"]=>
array(0) {
}
}
if you are using the json_decode version it will convert arrays to objects too:
object(stdClass)#2 (2) {
["item_1"]=>
object(stdClass)#3 (2) {
["item_1_1"]=>
object(stdClass)#4 (1) {
["item_1_1_1"]=>
string(5) "Hello"
}
["item_1_2"]=>
string(5) "World"
}
["item_2"]=>
array(0) {
}
}
NOTE: just the empty array will be an array here.
To Answer your question: The best practice depends on what YOU need.
It depends, really: if you are working on data that might be an array in one case, and an object the next, it would probably be best to use the json_decode trick, simply because unlike a cast, its result is "recursive". There is one very important thing to keep in mind here, though: numeric indexes can, and probably will cause problems for you at some point in time. Take a look at this bug report
This is documented here, but not in a way that really stands out:
If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible;
Exampe of the problem:
$data = [
'foo' => 'bar',
123 => 'all is well',
];
$obj = json_decode(json_encode($data));
var_dump($obj->foo);//bar
var_dump($obj->{123});//all is well
$cast = (array) $obj;
var_dump($cast);//shows both keys
var_dump(isset($cast[123]));//FALSE!!!
var_dump(isset($cast['123']));//FALSE
Basically: If you start converting arrays to objects and back again, numeric keys are not reliable anymore. If I were you, I'd simply change the code that is passing the data where possible, or I'd create a value object that can be set using an array or an object, and normalize the data that way.
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.
What is the name of the integer between brackets in a var_dump of an object. And how do I acces it with PHP?
I'm referring to the (3) in the next example.
object(SimpleXMLElement)#18 (3) {
["ID"]=>
string(3) "xx"
["Name"]=>
string(25) "xx"
["Date"]=>
string(10) "xx"
}
this is the number of properties of an object. to count this, you can cast your object to an array and use count():
$number = count((array)$object);
EDIT: i did a small test (see at codepad) wich prooves that casting to an array is what you want to do instead of using get_object_vars() as others mentioned because the later one doesn't count private properties while array-casting as well as var_dump do count these.
It's the number of public properties of that object, and isn't directly accessible
What is the name of the integer between brackets in a var_dump of an object. And how do I acces it with PHP?
I'm referring to the (3) in the next example.
That's the number of public members it has (namely, ID, Name and Date). If you want to know that number, you could just use count( get_object_vars( $object ) ):
<?php
$foo = new stdClass;
$foo->foo = 42;
$foo->bar = 42;
$foo->baz = 42;
var_dump( count( get_object_vars( $foo ) ) );
I have an object as this:
object(stdClass)#27 (1)
{
[0] => object(stdClass)#26 (6)
{
["_id"] => object(MongoId)#24 (1)
{
["$id"] => string(24) "4e6ea439caa47c2c0c000000"
}
["username"] => string(16) "wdfkewkghbrjkghb"
["email"]=> string(24) "wdhbfjkwhegerg#€rg.efg"
["password"]=> string(32) "4297f44b13955235245b2497399d7a93"
["slug"]=> string(16) "wdfkewkghbrjkghb"
["insert_datetime"]=> string(19) "2011-09-13 12:09:49"
}
}
I assign this object to $user.
I can't get access on this object properties doing $user->username cause I receive the message:
Undefined property: stdClass::$username
Then if I do var_dump(get_object_vars($user)) it returns an empty array.
How do I grab the properties? I don't want to use loops if I can avoid it.
The process is this:
Retrieve results from mongo_db:
$returns = array();
while ($documents->hasNext())
{
if ($this->CI->config->item('mongo_return') == 'object')
{
$returns[] = (object) $documents->getNext();
}
if ($this->CI->config->item('mongo_return') == 'array')
{
$returns[] = (array) $documents->getNext();
}
}
if ($this->CI->config->item('mongo_return') == 'object')
{
return (object)$returns;
}
if ($this->CI->config->item('mongo_return') == 'array')
{
return $returns;
}
passing data to model
function populateBy($what = false) {
return $this->mongo_db
->where($what)
->get($this->tb['users']);
}
definitely grab results in controller:
$what = array(
'email'=>$email,
'password'=>$password,
'confirm'=>'1'
);
$user = $this->model_user->populateBy($what);
As gilden says, the property you're looking for is a property of a subobject. However, he missed that object property access is not the same as array element access.
The real problem you're facing here is that you've converted an array to object, and now you have a numeric property name. To get to properties you have to use syntax like $user->0->username, but clearly this is not valid as 0 is not a valid variable name.
From the documentation:
If an object is converted to an array, the result is an array whose
elements are the object's properties. The keys are the member variable
names, with a few notable exceptions: integer properties are
unaccessible [sic]; private variables have the class name prepended to the
variable name; protected variables have a '*' prepended to the
variable name. These prepended values have null bytes on either side.
This can result in some unexpected behaviour:
The function get_object_vars converts back into an array again so that it appears to work, but in fact anything could happen: the behaviour is unspecified because the object elements were rendered inaccessible in the intermediate stage. Similarly, $user->{'0'}->username may work for you but I would avoid it.
Unfortunately this means that you'll have to change the way your code works: do not convert a numerically-indexed array to an object.
Your username property is not where you're looking for it. Try
$username = $user[0]->username;
EDIT Trying this gives me some unexpected results. I get "Cannot use object of type stdClass as array" so what I think you should do is using a foreach loop
// $users is the object in this sample
foreach($users as $user)
{
$username = $user->username;
}
EDIT 2 You could use get_object_vars
$users = get_object_vars($users);
$username = $users[0]->username;
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?