I need to check if an array of DOMNode objects contains all the items in a similar array of DOMNode objects.
In general, to check if an array contains another array, I've tried some of the methods outlined in this question. However, both array_intersect() and array_diff() compare array items on the bases (string) $elem1 === (string) $elem2 - which throws the following error for DOMElements as they can't be converted to strings.
PHP Catchable fatal error:
Object of class DOMElement could not be converted to string in...
What would be the proper way of handling this?
I've made this which seems to work, as example i filled both arrays with all kinds of objects and types just to see if it works:
$array = array(new DOMDocument(), 'foobar', 112312, new DateTime('Y'));
$array2 = array(new DOMDocument(), 'foobar',12312, false, new DateTime('Y'), 112312, true);
var_dump(array_diff_two($array,$array2)); //returns true
$array = array(new DOMDocument(), 'foobar', 112312, new DateTime('m'));
$array2 = array(new DOMDocument(), 'lorem ipsum!',12312, false, new DateTime('Y'), 112312, true);
var_dump(array_diff_two($array,$array2)); //returns false
function array_diff_two($array1, $array2){
// serialize all values from array 2 which we will check if they contain values from array 1
$serialized2 = array();
foreach ($array2 as $value){
$serialized2[] = serialize($value);
}
// Check if all values from array 1 are in 2, return false if it's not found
foreach ($array1 as $value) {
if (! in_array(serialize($value), $serialized2)) {
return false;
}
}
return true;
}
As I've written it now, here's an alternative solution. Tim's solution is more readable, in my opinion.
//Does array of DOMNodes contain other array DOMNodes
private function array_contains_array($haystack,$needle){
//Create object hash array of $haystack
$haystackHashArr = array();
foreach ($haystack as $idx => $haystackObj) {
$haystackHashArr[$idx] = spl_object_hash($haystackObj);
}
//Now search for hashes of needle array objects in Haystack-hash-Array
foreach ($needle as $domNode) {
$huntedForHash = spl_object_hash($domNode);
foreach($haystackHashArr as $hsHash){
if ($hsHash == $huntedForHash) continue 2;
}
//Only get here if an item not found (Due to continue statement)
return false;
}
return true;
}
Related
I have a function that searches a multidimensional array for a key, and returns the path
inside the array to my desired key as a string.
Is there any way I can use this string in php to reach this place in my original array, not to get to the value but to make changes to this specific bracnch of the array?
An example:
$array = array('first_level'=>array(
'second_level'=>array(
'desired_key'=>'value')));
in this example the function will return the string:
'first_level=>second_level=>desired_key'
Is there a way to use this output, or format it differently in order to use it in the following or a similar way?
$res = find_deep_key($array,'needle');
$array[$res]['newkey'] = 'injected value';
Thanks
If the keys path is safe (e.g. not given by the user), you can use eval and do something like:
$k = 'first_level=>second_level=>desired_key';
$k = explode('=>', $k);
$keys = '[\'' . implode('\'][\'', $k) . '\']';
eval('$key = &$array' . $keys . ';');
var_dump($key);
I think you want to do a recursive search in the array for your key? Correct me if i am wrong.
Try this
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
Taken from here http://in3.php.net/manual/en/function.array-search.php#91365
You need something like:
find_key_in_array($key, $array, function($foundValue){
// do stuff here with found value, e.g.
return $foundValue * 2;
});
and the implementation would be something like:
function find_key_in_array($key, $array, $callback){
// iterate over array fields recursively till you find desired field, then:
...
$array[$key] = $callback($array[$key]);
}
If you need to append some new sub-array into multidimensional complex array and you know where exactly it should be appended (you have path as a string), this might work (another approach without eval()):
function append_to_subarray_by_path($newkey, $newvalue, $path, $pathDelimiter, &$array) {
$destinationArray = &$array;
foreach (explode($pathDelimiter, $path) as $key) {
if (isset($destinationArray[$key])) {
$destinationArray = &$destinationArray[$key];
} else {
$destinationArray[$newkey] = $newvalue;
}
}
}
$res = find_deep_key($array,'needle');
append_to_subarray_by_path('newkey', 'injected value', $res, '=>', $array);
Of course, it will work only if all keys in path already exist. Otherwise it will append new sub-array into wrong place.
just write a function that takes the string and the array. The function will take the key for each array level and then returns the found object.
such as:
void object FindArray(Array[] array,String key)
{
if(key.Length == 0) return array;
var currentKey = key.Split('=>')[0];
return FindArray(array[currentKey], key.Remove(currentKey));
}
I think I could do this with a foreach loop like this:
foreach ($haystack as $item)
if (isset($item->$needle_field) && $item->$needle_field == $needle)
return true;
}
but i was wandering if it could be done without a loop?
something like:
if(in_array($item->$needle_field == $needle,$haystack)
return true;
Yes, in modern PHP you can determine if a specific object property contains a specific value without a classic loop by combining the forces of array_column() (which has evolved to also handle arrays of objects) and in_array().
Code: (Demo)
$objects = [
(object)['cats' => 2],
(object)['dogs' => 2],
(object)['fish' => 10],
(object)['birds' => 1],
];
$needleField = 'cats';
$needleValue = 2;
var_export(
in_array($needleValue, array_column($objects, $needleField))
);
// output: true
The advantage of this technique is the obviously concise syntax. This is a perfectly acceptable approach for relatively small volumes of data.
A possible disadvantage to this technique is that array_column() will be generating a new array of all of values that relate to the $needleField.
In my above demo, array_column() will only generate a single-element array because there is only one cats property in all of the objects. If we were processing a relatively large volume of data, then it would be inefficient to bother collecting all of the qualifying cats values and then run in_array() when only one match is necessary to return true.
For "large" volumes of data where performance is a primary criterion for script design, a classic foreach loop would be a better choice and as soon as an object satisfies the rules, then the loop should be halted via return or break.
Code: (Demo)
function hasPropertyValue(array $objects, $property, $value): bool {
foreach ($objects as $object) {
if (property_exists($object, $property) && $object->{$property} === $value) {
return true;
}
}
return false;
}
var_export(
hasPropertyValue($objects, $needleField, $needleValue)
);
It's possible, but it's not any better:
<?php
function make_subject($count, $success) {
$ret = array();
for ($i = 0; $i < $count; $i++) {
$object = new stdClass();
$object->foo = $success ? $i : null;
$ret[] = $object;
}
return $ret;
}
// Change true to false for failed test.
$subject = make_subject(10, true);
if (sizeof(array_filter($subject, function($value) {
return $value->foo === 3;
}))) {
echo 'Value 3 was found!';
} else {
echo 'Value 3 was not found.';
}
Outputs Value 3 was found!.
I advise you remain with the for loop: it's legible, unlike any tricks to save a line that you might find.
This will not work if the array you are searching is out of your control. But, if you are the one building the array of objects to be searched, you can structure it using the needle as array keys to be used with array_key_exists when you are searching.
For example, instead of making your $haystack array like this:
[
{
'needle_field' => $needle
},
...
]
Make it like this:
[
$needle => {
'needle_field' => $needle
},
...
]
And search like this:
if (array_key_exists($needle, $haystack)) {
return true;
}
Finally, if you need to, you can convert back to an integer indexed array by using array_values
$haystack = array_values($haystack);
This may not work in all situations but it worked great for me.
Maybe with array_key_exists:
if (array_key_exists($needle_field, $haystack) {
if ($haystack[$needle_field] == $needle) {
echo "$needle exists";
}
}
All my AJAX requests are in json format which are being parsed in javascript.
How can i prevent null values being displayed in an HTML page without needing to write an if-statement in javascript for each json value?
Or should i write a custom PHP function to encode an array to json instead of using json_encode() so it doesn't display 'null' ?
In server side with PHP,
You can use array_filter before json_encode.
array_filter without second argument removes null elements of entry array, example :
$object= array(
0 => 'foo',
1 => false,
2 => -1,
3 => null,
4 => ''
);
$object = (object) array_filter((array) $object);
$result = json_encode($object);
The $result contains:
{"0":"foo","2":-1}
As you see, the null elements are removed.
I'm going to add to the accepted answer because that will only work if you have a 1 dimensional object or array. If there is any array or object nesting then in order to get the accepted solution to work, you must create some sort of recursive array filter. Not ideal.
The best solution my colleague and I came up with was to actually perform a regular expression on the JSON string before it was returned from the server.
$json = json_encode($complexObject);
echo preg_replace('/,\s*"[^"]+":null|"[^"]+":null,?/', '', $json);
The regular expression will remove all places in the string of the form ,"key":null including any whitespace between the leading comma and the start of the key. It will also match "key":null, afterwards to make sure that no null values were found at the beginning of a JSON object.
This isn't an ideal solution but it's far better than creating a recursive array filter given an object could be several dimensions deep. A better solution would be if json_encode had a flag that let you specify if you wanted null entries to remain in the output string.
To remove only NULL, but keep FALSE, '', and 0:
function is_not_null($var)
{
return !is_null($var);
}
echo json_encode(array_filter((array) $object, 'is_not_null'));
public function __toString() {
$obj = clone $this;
$keys = get_object_vars($obj);
foreach ($keys as $key => $value) {
if (!$value) {
unset($obj->{$key});
}
}
return json_encode($obj);
}
What about using the native JSON.stringify method on the javascript side?
You can set, a second parameter, a function to remove keys with a null value.
If you return undefined, the property is not included in the output JSON string (check the documentation for "the replacer parameter" at https://developer.mozilla.org/en-US/docs/Using_native_JSON#The_replacer_parameter).
function removeNulls(obj) {
var str = JSON.stringify(obj, function(key, val) {
if (val == null) return undefined;
return val;
});
return JSON.parse(str);
}
Then you can have a "normalized" JSON object by calling:
var obj = removeNulls(obj);
echo json_encode(array_filter((array) $object, function($val) {
return !empty($val);
}));
class Foo implements JsonSerializable
{
public $param1;
public $param2;
public function jsonSerialize()
{
return array_filter((array) $this, function ($var) {
return !is_null($var);
});
}
}
public function testJsonSerialization()
{
$expectedJson = '{"param1":true}';
$foo = new Foo();
$foo->param1 = true;
$foo->param2 = null;
$this->assertEquals(
$expectedJson,
json_encode($foo)
);
}`
of course you can create Abstract class with custom jsonSerialize method and extend all your DTOs from this
A version that works with multi-dimensional arrays.
$withoutNull = function($a) use (&$withoutNull) {
return array_filter(
array_map(
fn($p) => is_array($p) ? $withoutNull($p) : $p, $a
)
);
};
Example:
$a = [
"name" => "nathan",
"data" => [
"age" => 27,
"something" => null
],
"another" => null
];
$b = $withoutNull($a);
print_r($b);
Output:
Array
(
[name] => nathan
[data] => Array
(
[age] => 27
)
)
Is there a way to convert a multidimensional array to a stdClass object in PHP?
Casting as (object) doesn't seem to work recursively. json_decode(json_encode($array)) produces the result I'm looking for, but there has to be a better way...
As far as I can tell, there is no prebuilt solution for this, so you can just roll your own:
function array_to_object($array) {
$obj = new stdClass();
foreach ($array as $k => $v) {
if (strlen($k)) {
if (is_array($v)) {
$obj->{$k} = array_to_object($v); //RECURSION
} else {
$obj->{$k} = $v;
}
}
}
return $obj;
}
I know this answer is coming late but I'll post it for anyone who's looking for a solution.
Instead of all this looping etc, you can use PHP's native json_* function. I've got a couple of handy functions that I use a lot
/**
* Convert an array into a stdClass()
*
* #param array $array The array we want to convert
*
* #return object
*/
function arrayToObject($array)
{
// First we convert the array to a json string
$json = json_encode($array);
// The we convert the json string to a stdClass()
$object = json_decode($json);
return $object;
}
/**
* Convert a object to an array
*
* #param object $object The object we want to convert
*
* #return array
*/
function objectToArray($object)
{
// First we convert the object into a json string
$json = json_encode($object);
// Then we convert the json string to an array
$array = json_decode($json, true);
return $array;
}
Hope this can be helpful
You and many others have pointed to the JSON built-in functions, json_decode() and json_encode(). The method which you have mentioned works, but not completely: it won't convert indexed arrays to objects, and they will remain as indexed arrays. However, there is a trick to overcome this problem. You can use JSON_FORCE_OBJECT constant:
// Converts an array to an object recursively
$object = json_decode(json_encode($array, JSON_FORCE_OBJECT));
Tip: Also, as mentioned here, you can convert an object to array recursively using JSON functions:
// Converts an object to an array recursively
$array = json_decode(json_encode($object), true));
Important Note: If you do care about performance, do not use this method. While it is short and clean, but it is the slowest among alternatives. See my other answer in this thread relating this.
function toObject($array) {
$obj = new stdClass();
foreach ($array as $key => $val) {
$obj->$key = is_array($val) ? toObject($val) : $val;
}
return $obj;
}
You can use the array_map recursively:
public static function _arrayToObject($array) {
return is_array($array) ? (object) array_map([__CLASS__, __METHOD__], $array) : $array;
}
Works perfect for me since it doesn't cast for example Carbon objects to a basic stdClass (which the json encode/decode does)
/**
* Recursively converts associative arrays to stdClass while keeping integer keys subarrays as arrays
* (lists of scalar values or collection of objects).
*/
function a2o( array $array ) {
$resultObj = new \stdClass;
$resultArr = array();
$hasIntKeys = false;
$hasStrKeys = false;
foreach ( $array as $k => $v ) {
if ( !$hasIntKeys ) {
$hasIntKeys = is_int( $k );
}
if ( !$hasStrKeys ) {
$hasStrKeys = is_string( $k );
}
if ( $hasIntKeys && $hasStrKeys ) {
$e = new \Exception( 'Current level has both integer and string keys, thus it is impossible to keep array or convert to object' );
$e->vars = array( 'level' => $array );
throw $e;
}
if ( $hasStrKeys ) {
$resultObj->{$k} = is_array( $v ) ? a2o( $v ) : $v;
} else {
$resultArr[$k] = is_array( $v ) ? a2o( $v ) : $v;
}
}
return ($hasStrKeys) ? $resultObj : $resultArr;
}
Some of the other solutions posted here fail to tell apart sequential arrays (what would be [] in JS) from maps ({} in JS.) For many use cases it's important to tell apart PHP arrays that have all sequential numeric keys, which should be left as such, from PHP arrays that have no numeric keys, which should be converted to objects. (My solutions below are undefined for arrays that don't fall in the above two categories.)
The json_decode(json_encode($x)) method does handle the two types correctly, but is not the fastest solution. It's still decent though, totaling 25µs per run on my sample data (averaged over 1M runs, minus the loop overhead.)
I benchmarked a couple of variations of the recursive converter and ended up with the following. It rebuilds all arrays and objects (performing a deep copy) but seems to be faster than alternative solutions that modify the arrays in place. It clocks at 11µs per execution on my sample data:
function array_to_object($x) {
if (!is_array($x)) {
return $x;
} elseif (is_numeric(key($x))) {
return array_map(__FUNCTION__, $x);
} else {
return (object) array_map(__FUNCTION__, $x);
}
}
Here is an in-place version. It may be faster on some large input data where only small parts need to be converted, but on my sample data it took 15µs per execution:
function array_to_object_inplace(&$x) {
if (!is_array($x)) {
return;
}
array_walk($x, __FUNCTION__);
reset($x);
if (!is_numeric(key($x))) {
$x = (object) $x;
}
}
I did not try out solutions using array_walk_recursive()
public static function _arrayToObject($array) {
$json = json_encode($array);
$object = json_decode($json);
return $object
}
Because the performance is mentioned, and in fact it should be important in many places, I tried to benchmark functions answered here.
You can see the code and sample data here in this gist. The results are tested with the data exists there (a random JSON file, around 200 KB in size), and each function repeated one thousand times, for the results to be more accurate.
Here are the results for different PHP configurations:
PHP 7.4.16 (no JIT)
$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive(): Completed in 0.000560s
pureRecursivePreservingIntKeys(): Completed in 0.000580s
jsonEncode(): Completed in 0.002045s
jsonEncodeOptimized(): Completed in 0.002060s
jsonEncodeForceObject(): Completed in 0.002174s
arrayMap(): Completed in 0.000561s
arrayMapPreservingIntKeys(): Completed in 0.000592s
arrayWalkInplaceWrapper(): Completed in 0.001016s
PHP 8.0.2 (no JIT)
$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive(): Completed in 0.000535s
pureRecursivePreservingIntKeys(): Completed in 0.000578s
jsonEncode(): Completed in 0.001991s
jsonEncodeOptimized(): Completed in 0.001990s
jsonEncodeForceObject(): Completed in 0.002164s
arrayMap(): Completed in 0.000579s
arrayMapPreservingIntKeys(): Completed in 0.000615s
arrayWalkInplaceWrapper(): Completed in 0.001040s
PHP 8.0.2 (tracing JIT)
$ php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=250M -dopcache.jit=tracing benchmark.php
pureRecursive(): Completed in 0.000422s
pureRecursivePreservingIntKeys(): Completed in 0.000410s
jsonEncode(): Completed in 0.002004s
jsonEncodeOptimized(): Completed in 0.001997s
jsonEncodeForceObject(): Completed in 0.002094s
arrayMap(): Completed in 0.000577s
arrayMapPreservingIntKeys(): Completed in 0.000593s
arrayWalkInplaceWrapper(): Completed in 0.001012s
As you see, the fastest method with this benchmark is pure recursive PHP functions (posted by #JacobRelkin and #DmitriySintsov), especially when it comes to the JIT compiler. When it comes to json_* functions, they are the slowest ones. They are about 3x-4x (in the case of JIT, 5x) slower than the pure method, which may seem unbelievable.
One thing to note: If you remove iterations (i.e. run each function only one time), or even strictly lower its count, the results would differ. In such cases, arrayMap*() variants win over pureRecursive*() ones (still json_* functions method should be the slowest). But, you should simply ignore these cases. In the terms of performance, scalability is much more important.
As a result, in the case of converting arrays to object (and vice versa?), you should always use pure PHP functions, resulting in the best performance, perhaps independent from your configurations.
The simpliest way to convert an associative array to object is:
First encode it in json, then decode it.
like $objectArray = json_decode(json_encode($associtiveArray));
Here's a function to do an in-place deep array-to-object conversion that uses PHP internal (shallow) array-to-object type casting mechanism.
It creates new objects only when necessary, minimizing data duplication.
function toObject($array) {
foreach ($array as $key=>$value)
if (is_array($value))
$array[$key] = toObject($value);
return (object)$array;
}
Warning - do not use this code if there is a risk of having circular references.
Here is a smooth way to do it that can handle an associative array with great depth and doesn't overwrite object properties that are not in the array.
<?php
function setPropsViaArray( $a, $o )
{
foreach ( $a as $k => $v )
{
if ( is_array( $v ) )
{
$o->{$k} = setPropsViaArray( $v, ! empty ( $o->{$k} ) ? $o->{$k} : new stdClass() );
}
else
{
$o->{$k} = $v;
}
}
return $o;
};
setPropsViaArray( $newArrayData, $existingObject );
Late, but just wanted to mention that you can use the JSON encoding/decoding to convert fully from/to array:
//convert object $object into array
$array = json_decode(json_encode($object), true);
//convert array $array into object
$object = json_decode(json_encode($array));
json_encode and json_decode functions are available starting from php 5.2
EDIT: This function is conversion from object to array.
From https://forrst.com/posts/PHP_Recursive_Object_to_Array_good_for_handling-0ka
protected function object_to_array($obj)
{
$arrObj = is_object($obj) ? get_object_vars($obj) : $obj;
foreach ($arrObj as $key => $val) {
$val = (is_array($val) || is_object($val)) ? $this->object_to_array($val) : $val;
$arr[$key] = $val;
}
return $arr;
}
I was looking for a way that acts like json_decode(json_encode($array))
The problem with most other recursive functions here is that they also convert sequential arrays into objects. However, the JSON variant does not do this by default. It only converts associative arrays into objects.
The following implementation works for me like the JSON variant:
function is_array_assoc ($arr) {
if (!is_array($arr)) return false;
foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
return false;
}
// json_decode(json_encode($array))
function array_to_object ($arr) {
if (!is_array($arr) && !is_object($arr)) return $arr;
$arr = array_map(__FUNCTION__, (array)$arr);
return is_array_assoc($arr) ? (object)$arr : $arr;
}
// json_decode(json_encode($array, true))
// json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
function object_to_array ($obj) {
if (!is_object($obj) && !is_array($obj)) return $obj;
return array_map(__FUNCTION__, (array)$obj);
}
If you want to have the functions as a class:
class ArrayUtils {
public static function isArrAssoc ($arr) {
if (!is_array($arr)) return false;
foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
return false;
}
// json_decode(json_encode($array))
public static function arrToObj ($arr) {
if (!is_array($arr) && !is_object($arr)) return $arr;
$arr = array_map([__CLASS__, __METHOD__], (array)$arr);
return self::isArrAssoc($arr) ? (object)$arr : $arr;
}
// json_decode(json_encode($array, true))
// json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
public static function objToArr ($obj) {
if (!is_object($obj) && !is_array($obj)) return $obj;
return array_map([__CLASS__, __METHOD__], (array)$obj);
}
}
If anyone finds any mistakes please let me know.
/**
* Convert a multidimensional array to an object recursively.
* For any arrays inside another array, the result will be an array of objects.
*
* #author Marcos Freitas
* #param array|any $props
* #return array|any
*/
function array_to_object($props, $preserve_array_indexes = false) {
$obj = new \stdClass();
if (!is_array($props)) {
return $props;
}
foreach($props as $key => $value) {
if (is_numeric($key) && !$preserve_array_indexes) {
if(!is_array($obj)) {
$obj = [];
}
$obj[] = $this->array_to_object($value);
continue;
}
$obj->{$key} = is_array($value) ? $this->array_to_object($value) : $value;
}
return $obj;
}
The shortest I could come up with:
array_walk_recursive($obj, function (&$val) { if (is_object($val)) $val = get_object_vars($val); });
From this question here, I was writing an enum wrapper to have some methods that can be used with lambdas to somewhat emulate ruby's usage of blocks in enums.
class enum {
public $arr;
function __construct($array) {
$this->arr = $array;
}
function each($lambda) {
array_walk($this->arr, $lambda);
}
function find_all($lambda) {
return array_filter($this->arr, $lambda);
}
function inject($lambda, $initial=null) {
if ($initial == null) {
$first = array_shift($this->arr);
$result = array_reduce($this->arr, $lambda, $first);
array_unshift($this->arr, $first);
return $result;
} else {
return array_reduce($this->arr, $lambda, $initial);
}
}
}
$list = new enum(array(-1, 3, 4, 5, -7));
$list->each(function($a) { print $a . "\n";});
// in PHP you can also assign a closure to a variable
$pos = function($a) { return ($a < 0) ? false : true;};
$positives = $list->find_all($pos);
Now, how could I implement inject() as elegantly as possible?
EDIT: method implemented as seen above. Usage examples:
// inject() examples
$list = new enum(range(5, 10));
$sum = $list->inject(function($sum, $n) { return $sum+$n; });
$product = $list->inject(function($acc, $n) { return $acc*$n; }, 1);
$list = new enum(array('cat', 'sheep', 'bear'));
$longest = $list->inject(function($memo, $word) {
return (strlen($memo) > strlen($word)) ? $memo : $word; }
);
I'm not familiar with Ruby, but from the description, it seems similar to array_reduce.
mixed array_reduce ( array $input , callback $function [, mixed $initial = NULL ] )
array_reduce() applies iteratively the function function to the elements of the array input, so as to reduce the array to a single value.
In addition to "reduce", this operation is also sometimes called "fold"; in Mathematica:
Fold[f, init, {a, b, c, d}] == f[f[f[f[init, a], b], c], d]
The second form uses the first element of the collection as a the initial value (and skips that element while iterating).
This second form can be implemented this way:
//$arr is the initial array
$first = array_shift($arr);
$result = array_reduce($arr, $callback, $first);
Response to Mladen
The array functions in PHP cannot be used that way because they can only work with arrays, not arbitrary objects.
There are a few options here:
You could convert the object into an array prior to passing it to array_reduce. In practice, this doesn't have much value because the conversion consists of creating an array with the object properties as elements. This behavior can only be changed internally (writing a native extension).
You could have all your objects implement an interface with a method toArray that would have to be called priorly to passing it to array_reduce. Not a great idea, either.
You could implement a version of array_reduce that works with any Traversable object. This would be easy to do, but you couldn't put a Traversable type hint in the function declaration since arrays are not objects. With such a hint, every array would have to be encapsulated in an ArrayIterator object prior to the function call.