Best way to ensure a PHP variable is an array - php

I have a javascript client passing a parameter to a server function (I'm using ExtJS Direct). Sometimes the client sends a single object, sometimes it sends an array of objects.
Currently I'm using this EnsureArray function to ensure the parameter is an array, and then I do foreach:
// Wraps a non array variable with an array.
function EnsureArray( &$aVar )
{
if ( !is_array( $aVar ) )
$var = array( $aVar );
}
function Destroy( $aRecords )
{
// $aRecords might be a single object or an array of objects. Ensure it's wrapped as an array.
$this->EnsureArray( $aRecords );
foreach ( $aRecords as $aRecord )
{
sql( "UPDATE Groups SET deleted = true WHERE id = $aRecord->id LIMIT 1" );
}
return array(
'success' => true,
);
}
Is there a trick, neater way, one line that can do the same?
Edit: Since posting this question, I've found that ExtJS has an option to send all records wrapped in array.

You could try the following, instead of the function:
$aRecords = is_array($aRecords) ? $aRecords : array($aRecords);

That's probably the best way tbh, if you're not going to enforce that you're always being sent an array.

I would make the function Destroy require arrays as its parameter:
function Destroy(array $aRecords) { ... }
The client should then also always send arrays. If, for whatever reason, that is not possible, the code that is receiving the (non-)array from the client and is passing it on to Destroy() needs to be responsible for passing it along as an array, because it's the interface between the (non-compliant) client and the standardized function.
There's probably going to be one endpoint for each possible action the client can call, so you don't even need to figure out whether the data is an array or not, you simply know. I.e.:
// someaction.php
include 'destroy.php';
$data = json_decode($_POST['data']);
Destroy($data);
but:
// anotheraction.php
include 'destroy.php';
$data = json_decode($_POST['data']);
Destroy(array($data));
If the client erratically sends different formats to the same action, fix the client.

Simply typecast the variable to an array:
function Destroy( $aRecords )
{
foreach ( (array)$aRecords as $aRecord )
{
sql( "UPDATE Groups SET deleted = true WHERE id = $aRecord->id LIMIT 1" );
}
return array(
'success' => true,
);
}
See http://php.net/manual/en/language.types.type-juggling.php

Related

What is register_rest_route's callback's $data variable?

This is really bizarre. I've spent at least 5 hours trying to figure out what this $data variable truly is and what is in it. I think the question is a little confusing, so let me explain what I'm talking about:
The callback for register_rest_route is a function that takes one parameter, $data, which seems to be a magical object. I've been trying to follow the source to figure out if there's something more to this. Could someone explain? It could be very illuminating for the community (OK, or at least me).
register_rest_route( 'custom/v1', '/customer/', array(
'methods' => 'POST',
'callback' => 'get_stuff',
) );
So, my function get_stuff looks like this:
get_stuff( $data ) {
return array(
'is object? ' . is_object( $data ), // returns 1
'is array? ' . is_array( $data ), // returns blank; nothing
get_object_vars( $data ), // returns an empty array
$data, // returns {}; an empty object
$data['assignee'], // returns POST'd data as expected
$data['number'], // returns POST'd data as expected
$data->number // returns null; I thought this was an object? Why doesn't this work?
}
What the is $data?
For the longest time, I was trying to return the entire $data object, just to test/debug/play, and was getting an empty object, until I tried getting one of the properties. I'm thoroughly confused because it doesn't behave like an object, but apparently is one. I can't seem to get it in its entirety, but only if I specify a property. Can someone clear this up?
The return type is a WP_REST_REQUEST object, so in order to get the body of the request you will have to invoke the get_body method.
function get_stuff( $data ) {
$result = $data->get_body(); //returns a string of the body
return $result;
}
However, if you're like me, you'll most likely be sending JSON in a POST request, so invoke the parse_json method to get an object/array.
function get_val($data){
$json_result = json_decode($data->get_body(), true); //note second param is for setting this to an associative array
$some_value = $json_result["some_var"];
return $some_value;
}
Here's some helpful links:
WP_REST_REQUEST docs: https://developer.wordpress.org/reference/classes/wp_rest_request/
Parsing JSON:
https://developer.wordpress.org/reference/functions/json_decode/

Passing an argument to callback function working on an array

I'm using an external class (Zebra_cURL) to execute multiple HTTP GET requests. It worked in the following way:
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
$items[$key]['size'] = strlen($result->body);
}
So my callback should fill up my $items array with more info for each url (in my case - size of the page). So there is a missing $key variable.
This class supports extra parameters in the callbacks ($moreimfo in my case). BUT as I understand the data passing to each callback will be always the same.
$result object containing the original url info ($result->info['url']). So I COULD use it to find needed array element. However this looks too inefficient in case the size of an array will be big enough.
I think that I should find how to pass an array member key information for EACH callback execution. Is it possible without modifying the original class?
If you use the url as key in the $items array the solution could be something like
<?php
$items = [
'url0'=>array('url' => 'url0'),
'url1'=>array('url' => 'url1'),
'url2'=>array('url' => 'url2'),
'url3'=>array('url' => 'url3'),
];
$curl = new Zebra_cURL();
$curl->get(
array_keys($items),
function($result) use (&$items) {
$key = $result->info['url'];
$items[$key]['size'] = strlen($result->body);
}
);
using an anymous function that "Imports" the $items array via reference.
While it doesn't solve the original problem of passing a reference to the according array element to the callback, the following should be very fast (as noted in the comments, PHP Arrays are implemented using a hashtable).
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$lookup=array();
foreach($lookup as $k=>$v) {
$lookup[$v['url']]=$k;
}
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
global $lookup,$items;
$items[$lookup[$result->info['url']]]['size'] = strlen($result->body);
}
Probably you may consider using an OOP-approach, with the callback as a method, then the global-izing of the arrays shouldn't be necessary if you use $this->anyMember

Having the variable names in func_get_args()

As this function can take unknown numbers of parameters:
function BulkParam(){
return func_get_args();
}
A print_r will print only the values, but how i can retrieve the variable names as well as the array key? For example:
$d1 = "test data";
$d2 = "test data";
$d3 = "test data";
print_r(BulkParam($d1, $d2, $d3));
It will print this:
Array
(
[0] => test data
[1] => test data
[2] => test data
)
But i want to have the variables name as the index name or key name of all arrays. Then the array would look like this:
Array
(
[d1] => test data
[d2] => test data
[d3] => test data
)
I'm sure the PHP elite will have so many problems with this solution but it does work (I'm using PHP 5.6) and honestly I don't care since I've done hacks far worst than this for prototyping in many of my Java projects.
function args_with_keys( array $args, $class = null, $method = null, $includeOptional = false )
{
if ( is_null( $class ) || is_null( $method ) )
{
$trace = debug_backtrace()[1];
$class = $trace['class'];
$method = $trace['function'];
}
$reflection = new \ReflectionMethod( $class, $method );
if ( count( $args ) < $reflection->getNumberOfRequiredParameters() )
throw new \RuntimeException( "Something went wrong! We had less than the required number of parameters." );
foreach ( $reflection->getParameters() as $param )
{
if ( isset( $args[$param->getPosition()] ) )
{
$args[$param->getName()] = $args[$param->getPosition()];
unset( $args[$param->getPosition()] );
}
else if ( $includeOptional && $param->isOptional() )
{
$args[$param->getName()] = $param->getDefaultValue();
}
}
return $args;
}
Using the PHP Reflections API, we get all the method parameters and align them with their numeric indexes. (There might be a better way to do that.)
To use simply type:
print_r( args_with_keys( func_get_args() ) );
I also added the ability to optionally return the method's optional parameters and values. I'm sure this solution is far from perfect, so you're mileage may vary. Do keep in mind that while I did make it so providing the class and method was optional, I do highly suggest that you specify them if you're going anywhere near a production environment. And you should try to avoid using this in anything other than a prototype setup to begin with.
Specify the class and method with:
print_r( args_with_keys( func_get_args(), __CLASS__, __FUNCTION__ ) );
You can not. Variable names are not passed into functions. Variables are placeholders local to a specific algorithm, they are not data and they do not make sense in another scope and are not passed around. Pass an explicitly named associative array if you need key-value pairs:
bulkParam(['d1' => $d1, 'd2' => $d2, ...]);
A shortcut for this is:
bulkParam(compact('d1', 'd2'));
Then use an array:
function bulkParam(array $params) {
foreach ($params as $key => $value) ...
}
As Mark mentions in the comments, sometimes you don't even have variables in the first place:
bulkParam('foo');
bulkParam(foo(bar(baz())));
Now what?
Or eventually you'll want to refactor your code and change variable names:
// old
$d1 = 'd1';
bulkParam($d1);
// new
$userName = 'd1';
bulkParam($userName);
Your application behaviour should not change just because you rename a variable.

Set multi-dimensional array through function parameters

I'm working on a simple session manager for my framework. Im trying to setup a more user friendly structure for the session data. Essentially my sessions are stored like this:
$app_name = "Some_App_Name";
$component = "notifications";
$key = "errors";
$value = "There was some error";
$_SESSION[$app_name][$component][$key] = $value;
The problem I am facing is creating this structure through parameters within the session class. I have a set method which should ideally set a session value. The $app_name as listed above is by default added to the session class through the constructor, but I need to find a simple way of taking the parameters passed in within the method and then creating the rest. A simple example:
// Where keys could be: $key1 = notifications, $key2 => "notices"
public static function set($key1,$key2,$value) {
$_SESSION[self::$app_name][$key1][$key2] = $value;
}
The above would work if I always have 4 parameters but in some cases I might only have 2 parameters. I could pass 2 parameters (both being an array) but I'm looking for a more streamlined approach (if such an approach exists).
With the creating of the structure and setting values I also need a similiar way of verifying if the value or last key exists:
// Where keys could be: $key1 = notifications, $key2 => "errors"
public static function exists($key1,$key2) {
if(isset($_SESSION[self::$app_name][$key1][$key2])) {
return true;
}
Any suggestions would greatly be appreciated.
$params = array(
"key1" => "value1",
"key2" => "value2",
"value" => "value"
);
public static function set($params = NULL) //default null if no value is passed
{
if (!self::exists($params)) return false;
$_SESSION[self::$app_name][$params["key1"]][$params["key2"]] = $value;
return true;
}
public static function exists($params = NULL)
{
if(isset($_SESSION[self::$app_name][$params["key1"]][$params["key2"]]))
{
return true;
}
return false;
}
In the light of assisting other members wanting to do something similiar, I want to strongly advise you against using this concept as off the bat it sounds like a good idea but your true issue comes with the management of the array itself. Working straight with the $_SESSION superglobal really is the more powerful option on the basis that:
Even with a parameter in place for say server and component ($_SESSION ['somename']['auth']), what happens when you want to access content from that level from another instance of the object? Say I have another session object instance for $_SESSION ['somename']['errors'] but need to access properties from $_SESSION ['somename']['auth'] but within scope my base within the session array is incorrect.
Adding properties is fine $this->session->add("key","name") but what if you want to append to that array (where name is actually an array and not just a value), or vise versa. Or checking for occurances if $_SESSION['somename']['auth']['key']['name'] actually has another key or value within it?
All and all having worked with this the last couple of days I can definately say that it might not be impossible to write a "fully functional" session manager class but at the end of the day for simplicity it's better to rather just work with the session array directly as it's less code and less issues as you go.

How can I call a function specified in a GET param?

I have a simple link like so: Accept
The idea is that this will run a function called wp_accept_function and pass in the id of 10 how do I do this? Thanks
Here is the code I have so far, but I feel I'm going wrong and need to pass the number into the function and then be able to use it within the function. Thanks
if ( isset ( $_GET ['wp_accept_function'] ) )
{
function wp_accept_favor ( $id )
{
// JAZZ
}
}
I think you want this:
First you need to define the function.
function wp_accept_favor($id) {
// do something
}
Then, you have to check if the parameter is set and call the function.
if (isset($_GET['wp_accept_function'])) {
// call the function passing the id casted to an integer
wp_accept_favor((int)$_GET['wp_accept_function']);
}
The cast to (int) is for avoid passing a non-integer type for wp_accept_favor() function, but you can handle it as you want.
If you are trying to build something generic...
// Add a white list of functions here which can be called via GET.
$safeFunctions = array('wp_accept_function');
foreach($_GET as $function => $argument) {
if (in_array($function, $safeFunctions)
AND function_exists($function)) {
$function($argument);
}
}
However, make sure you have a whitelist of safe functions, otherwise your app will no doubt have security issues.

Categories