Viewed   183 times

I'm integrating an API to my website which works with data stored in objects while my code is written using arrays.

I'd like a quick-and-dirty function to convert an object to an array.

 Answers

2

Just typecast it

$array = (array) $yourObject;

From Arrays:

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

Example: Simple Object

$object = new StdClass;
$object->foo = 1;
$object->bar = 2;

var_dump( (array) $object );

Output:

array(2) {
  'foo' => int(1)
  'bar' => int(2)
}

Example: Complex Object

class Foo
{
    private $foo;
    protected $bar;
    public $baz;

    public function __construct()
    {
        $this->foo = 1;
        $this->bar = 2;
        $this->baz = new StdClass;
    }
}

var_dump( (array) new Foo );

Output (with s edited in for clarity):

array(3) {
  'Foofoo' => int(1)
  '*bar' => int(2)
  'baz' => class stdClass#2 (0) {}
}

Output with var_export instead of var_dump:

array (
  '' . "" . 'Foo' . "" . 'foo' => 1,
  '' . "" . '*' . "" . 'bar' => 2,
  'baz' =>
  stdClass::__set_state(array(
  )),
)

Typecasting this way will not do deep casting of the object graph and you need to apply the null bytes (as explained in the manual quote) to access any non-public attributes. So this works best when casting StdClass objects or objects with only public properties. For quick and dirty (what you asked for) it's fine.

Also see this in-depth blog post:

  • Fast PHP Object to Array conversion
Sunday, August 14, 2022
1

Actually, this can be done. Through a php extension.

File: config.m4

PHP_ARG_ENABLE(test, whether to enable test Extension support, [ --enable-test   Enable test ext support])

if test "$PHP_TEST" = "yes"; then
  AC_DEFINE(HAVE_TEST, 1, [Enable TEST Extension])
  PHP_NEW_EXTENSION(test, test.c, $ext_shared)
fi

File: php_test.h

#ifndef PHP_TEST_H
#define PHP_TEST_H 1

#define PHP_TEST_EXT_VERSION "1.0"
#define PHP_TEST_EXT_EXTNAME "test"

PHP_FUNCTION(getaddress4);
PHP_FUNCTION(getaddress);

extern zend_module_entry test_module_entry;
#define phpext_test_ptr &test_module_entry

#endif

File: test.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_test.h"

ZEND_BEGIN_ARG_INFO_EX(func_args, 1, 0, 0)
ZEND_END_ARG_INFO()

static function_entry test_functions[] = {
    PHP_FE(getaddress4, func_args)
    PHP_FE(getaddress, func_args)
    {NULL, NULL, NULL}
};

zend_module_entry test_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_TEST_EXT_EXTNAME,
    test_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_TEST_EXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_TEST
ZEND_GET_MODULE(test)
#endif

PHP_FUNCTION(getaddress4)
{
    zval *var1;
    zval *var2;
    zval *var3;
    zval *var4;
    char r[500];
    if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aaaa", &var1, &var2, &var3, &var4) == FAILURE ) {
      RETURN_NULL();
    }
    sprintf(r, "n%p - %p - %p - %pn%p - %p - %p - %p", var1, var2, var3, var4, Z_ARRVAL_P(var1), Z_ARRVAL_P(var2), Z_ARRVAL_P(var3), Z_ARRVAL_P(var4) );
    RETURN_STRING(r, 1);
}

PHP_FUNCTION(getaddress)
{
    zval *var;
    char r[100];
    if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &var) == FAILURE ) {
      RETURN_NULL();
    }
    sprintf(r, "%p", Z_ARRVAL_P(var));
    RETURN_STRING(r, 1);
}

Then all you have to do is phpize it, config it, and make it. Add a "extension=/path/to/so/file/modules/test.so" to your php.ini file. And finally, restart the web server, just in case.

<?php
  $x = array("123"=>"123");
  $w = $x;
  $y = $x;
  $z = &$x;
  var_dump(getaddress4($w,$x,$y,$z));
  var_dump(getaddress($w));
  var_dump(getaddress($x));
  var_dump(getaddress($y));
  var_dump(getaddress($z));
?>

Returns(at least for me, your memory addresses will probably be different)

string '
0x9efeb0 - 0x9effe0 - 0x9ef8c0 - 0x9efeb0
0x9efee0 - 0x9f0010 - 0x9ed790 - 0x9efee0' (length=84)

string '0x9efee0' (length=8)

string '0x9f0010' (length=8)

string '0x9ed790' (length=8)

string '0x9efee0' (length=8)

Thanks to Artefacto for pointing this out, but my original code was passing the arrays by value, so thereby was recreating arrays including the referenced-one, and giving you bad memory values. I have since changed the code to force all params to be passed by reference. This will allow references, arrays, and object, to be passed in unmolested by the php engine. $w/$z are the same thing, but $w/$x/$y are not. The old code, actually showed the reference breakage and the fact that the memory addresses would change or match when all variables were passed in vs multiple calls to the same function. This was because PHP would reuse the same memory when doing multiple calls. Comparing the results of the original function would be useless. The new code should fix this problem.

FYI - I'm using php 5.3.2.

Sunday, September 4, 2022
 
sk0x50
 
3

You can try below code to merge array. Code generates desired output required to you. I have used sample array as given by you:

<?php
    $arr1=array(
        "384"=>array("name"=>"SomeMovieName1","age"=>"12.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>""),
        "452"=>array("name"=>"SomeMovieName2","age"=>"15.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>""),
        "954"=>array("name"=>"SomeMovieName3","age"=>"4.2 hrs","IMDBLink"=>"","IMDBRating"=>"", "coverArt"=>"")
    );
    $arr2=array(
       "384" => array("IMDBLink" => "7.2", "IMDBRating" => "http://www.imdb.com/LinkToMovie1", "coverArt" => "http://www.SomeLinkToCoverArt.com/1"),
       "452" => array("IMDBLink" => "5","IMDBRating" => "http://www.imdb.com/LinkToMovie2", "coverArt" => "http://www.SomeLinkToCoverArt.com/2"),
       "954"=>array("IMDBLink" => "8","IMDBRating" => "http://www.imdb.com/LinkToMovie3", "coverArt" => "http://www.SomeLinkToCoverArt.com/3")
    );
    $arr3 = array();
    foreach($arr1 as $key=>$val)
    {
         $arr3[] = array_merge($val, $arr2[$key]);
    }
    echo "<pre>";
    print_r($arr3);
?>
Tuesday, September 13, 2022
4

Using a simple regex via preg_match_all and array_combine is often the shortest and quickest option:

 preg_match_all("/([^\\]+)\\([^\\]+)/", $string, $p);
 $array = array_combine($p[1], $p[2]);

Now this is of course a special case. Both keys and values are separated by a backslash, as are all pairs of them. The regex is also a bit lengthier due to the necessary double escaping.

However this scheme can be generalized to other key:value,-style strings.

Distinct key:value, separators

Common variations include : and = as key/value separators, and , or & and others as pair delimiters. The regex becomes rather obvious in such cases (with the /x flag for readability):

 #                    ?    ?    ?
 preg_match_all("/ ([^:]+) : ([^,]+) /x", $string, $p);
 $array = array_combine($p[1], $p[2]);

Which makes it super easy to exchange : and , for other delimiters.

  • Equal signs = instead of : colons.
  • For example \t as pair delimiter (tab-separated key:value lists)
  • Classic & or ; as separator between key=value pairs.
  • Or just \s spaces or \n newlines even.

Allow varying delimiters

You can make it more flexible/forgiving by allowing different delimiters between keys/values/pairs:

 #                    ?      ?       ?
 preg_match_all("/ ([^:=]+) [:=]+ ([^,+&]+) /x", $string, $p);

Where both key=value,key2:value2++key3==value3 would work. Which can make sense for more human-friendlinies (AKA non-technical users).

Constrain alphanumeric keys

Oftentimes you may want to prohibit anything but classic key identifiers. Just use a w+ word string pattern to make the regex skip over unwanted occurences:

 #                   ?   ?    ?
 preg_match_all("/ (w+) = ([^,]+) /x", $string, $p);

This is the most trivial whitelisting approach. If OTOH you want to assert/constrain the whole key/value string beforehand, then craft a separate preg_match("/^(w+=[^,]+(,|$))+/", …

Strip spaces or quoting

You can skip a few post-processing steps (such as trim on keys and values) with a small addition:

 preg_match_all("/ s*([^=]+) s*=s* ([^,]+) (?<!s) /x", $string, $p);

Or for instance optional quotes:

 preg_match_all("/ s*([^=]+) s*=s* '? ([^,]+) (?<![s']) /x", $string, $p);

INI-style extraction

And you can craft a baseline INI-file extraction method:

 preg_match_all("/^ s*(w+) s*=s* ['"]?(.+?)['"]? s* $/xm", $string, $p);

Please note that this is just a crude subset of common INI schemes.

Alternative: parse_str()

If you have a key=value&key2=value2 string already, then parse_str works like a charm. But by combining it with strtr can even process varying other delimiters:

 #                         ??    ??
 parse_str(strtr($string, ":,", "=&"), $pairs);

Which has a couple of pros and cons of its own:

  • Even shorter than the two-line regex approach.
  • Predefines a well-known escaping mechanism, such as %2F for special characters).
  • Does not permit varying delimiters, or unescaped delimiters within.
  • Automatically converts keys[]= to arrays, which you may or may not want though.

Alternative: explode + foreach

You'll find many examples of manual key/value string expansion. Though this is often more code. explode is somewhat overused in PHP due to optimization assumptions. After profiling often turns out to be slower however due to the manual foreach and array collection.

Monday, October 10, 2022
 
1

The you can do this using compact:

function myFunc($a, $b, $c) {
    $params = compact('a', 'b', 'c');
    // ...
}

Or, get_defined_vars() will give you an associative array of all the variables defined in that scope, which would work, but I think this might also include $_POST, $_GET, etc...

Otherwise, you can use func_get_args to get a list of all the arguments passed to the function. This is not associative though, since it is only data which is passed (that is, there's no variable names). This also lets you have any number of arguments in your function.

Or, just specify one argument, which is an array:

function myFunc($params) {

}

compact() seems to be closest to what you're after though.

Monday, October 24, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :