Recursively cast to array in PHP

I recently ran into an issue where JSON encoding some objects in my code wasn’t working properly. After experimenting, I realized that casting everything to an array before JSON encoding magically fixed things. 

Casting an object to an array is simple enough:

$variable_to_array = (array) $object_var;

But, what happens when an object or array contains references to other objects or arrays? The answer is that we then need to recursively cast a given input to an array. But, we don’t necessarily want to recursively cast everything to an array. For example, this is what happens when we cast 1 to an array:

return (array) 1;
=> array(1) {
  [0]=>
  int(1)
}

A simple fix is to recursively cast non-scalar values to an array. Here’s an example of how we would do that:

/**
 * Given mixed input, will recursively cast to an array if the input is an array or object.
 *
 * @param mixed $input Any input to possibly cast to array.
 * @return mixed
 */ 
function recursive_cast_to_array( $input ) {
	if ( is_scalar( $input ) ) {
		return $input;
	}

	return array_map( 'recursive_cast_to_array', (array) $input );
}

Sanitize array of integers in PHP

I was recently working on a pull request where I wanted to take an array of object IDs and convert that into a string to be used in a SQL statement.

But, to prevent potential issues such as SQL injection, I needed to find a way to sanitize the array of integers and make sure that we only had integers in the array. Here is the solution that I used.

// Sanitize so that the array only has integer values.
$sanitized_array = array_map( 'intval', $ids );

// Now that we have all integers in our array, we can safely implode the
// array to be used in a SQL statement.
$ids_string = implode( ', ', $sanitized_array );
$sql = "SELECT * FROM {$table} WHERE {$object_id_column} IN ( {$ids_string} ) AND meta_key = %s";

If you’re not used to mapping an array of values, you may be wondering what the heck array_map() is doing. What array_map() does is iterate over an array (the second argument) and apply a callback function (the first argument) to each value in the array.

We then get back the resulting array with each value being sanitized by intval().

Alternative solution to sanitizing an array of integers

If you’re still a bit confused about what array_map( 'intval', $ids ), here’s another way to approach the issue:

// Sanitize so that the array only has integer values.
$sanitized_array = array();
foreach( $ids as $id ) {
  $sanitized_array[] = intval( $id );
}

// Now that we have all integers in our array, we can safely implode the
// array to be used in a SQL statement.
$ids_string = implode( ', ', $sanitized_array );
$sql = "SELECT * FROM {$table} WHERE {$object_id_column} IN ( {$ids_string} ) AND meta_key = %s";

In both examples, we iterate over the $ids array and apply the intval() function to each value.

Do while false in PHP?

I was working on something in Jetpack recently, and was mind-boggled when I came across a do…while( false ) loop.

Animated What Gif

What confused me so much, is why even use the loop if the condition was always false? Not able to figure out what this bit of code did, I brought the issue up to my team and Dan Walmsley was able to track it down.

Turns out, that in the initial commit, we were using the do...while( false ) loop as something of a poor man’s go-to.

If you look at loop in the initial commit, you’ll see that there are several break statements. So, at any point in time, we could break out of the loop and prevent any other code in the loop being reached. In effect, we would jumping to a spot just out of the loop – hence, goto.

To do while false or not?

While I understand the construct now, it did confuse the hell out of me at first. For that reason alone, I’m not sure it should be used. If you are going to use it, I would suggest at least writing a comment so the next developer has an idea of what’s going on.

Print PHP Stack Trace

I was having a bit of trouble tracking down exactly where a method was getting called from today.

Usually a search in my project directory will turn up the result very quickly, but I wasn’t having luck with PHPStorm for some reason.

So, the next best thing was to get a stack trace so that I could get the last few functions that had been run. I don’t do stack traces much in PHP, so I had to track down the code to use.

Of the few options to use, this seemed to be the easiest to use and remember.

$e = new Exception;

// Output the stack trace to the browser
echo $e->getTraceAsString();

// Send stack trace to error log
error_log( $e->getTraceAsString() );

And if you’re wanting more verbose output, including the arguments that were passed to each function, try this.

$e = new \Exception;

// Recursively print the stack trace
print_r( $e->getTrace() );

// Log the stack trace object
error_log( print_r( $e->getTrace(), true ) );