Having the variable names in func_get_args() - php

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.

Related

How do I match the value of an array to a key of another array in PHP?

I have 2 arrays, one of which is built dynamically and one which I use as a map.
The map array:
$social_links_map = [
'twitter' => 'twitter-icon',
'facebook' => 'facebook-icon'
];
And my dynamic array, which is just a simple list of social links:
$dynamic_social_links = [ 'twitter.com/me', 'facebook.com/me' ];
$social_links = explode( ',', $dynamic_social_links );
The $dynamic_social_links is an user input, as such, their input can be wrongly typed.
And I need to check if within my $social_links exists any of the $social_links_map keys and return the $social_links_map item accordingly:
if( !empty( $social_links ) ) {
foreach( $social_links_map as $handle => $icon ) {
foreach( $social_links as $social_link ) {
if( strpos( $social_link, $handle ) ) {
echo $handle;
}
}
}
}
This doesn't allow for "duplicate removal", nor does it look very pretty. Any ideas?
use
array_unique
to remove "duplicated data".
Try
$social_links = array_unique(explode( ',', $dynamic_social_links ));
Firstly, is it possible that you can allow the user to select from a list of your $social_links_map?
If not then the approach you are using is likely the simplest most readable way of doing this as there is no sure fire way to be able to match potentially random user input with a predefined array of options.
A couple of things to note:
As mentioned by le Mandarin, you can use array_unique to get rid of duplicates.
The other is that strpos will return 0 (which is falsey) if the search string (needle) is found at the very start of the context variable (haystack).
eg:
strpos('twitter.link/stuff', 'twitter');
will return 0 which is false Causing your if statement to fail.
instead try something like
if (false !== strpos('twitter.link/stuff', 'twitter')) {
Take note of the extra = in !==. This is necessary because you are saying "If the result is not exactly the boolean false.

Using variables for name and arguments to create a new instance of a class in PHP [duplicate]

This question already has answers here:
PHP passing parameters while creating new object, call_user_func_array for objects
(6 answers)
Closed 5 years ago.
I'm trying to call a class function that creates a new instance of a subclass.
I'm doing this inside a foreach loop with the following variables for the sub class name and arguments:
$classname = 'Element_Radio';
$classargs = array( $a, $b ); //Might have up to 4 arguments
Here's the original line of code I'm trying to execute, without any of the above variables:
$form->addElement(new Element_Radio($required, $required, $optional_array, $optional_array);
So first I tried:
$form->addElement( new $classname ($classargs) );
But I think I need something like this:
$form->addElement( call_user_func_array(new $classname,$classargs) );
Either way, I'm getting errors of:
"Warning: Missing argument 2 for Element::__construct() ..."
So it looks like the arguments are getting passed in as one array variable, and not separately.
I ended up writing a bunch of if statements to just make the function call depending on the values of $classargs, but I'm wondering if there is a programmatic way of doing what I want without the IFs.
EDIT - SOLUTION with the code I added to account for the fact that my array of arguments was a multidimensional array that didn't have all numeric indexes. The splat operator (...) only works with an array with numeric indexes.
$classname = 'Element_Radio';
$classargs = array();
if ( isset( $a ) ) { array_push($classargs, $a); }
if ( isset( $b ) ) { array_push($classargs, $b); }
if ( isset( $c ) ) { array_push($classargs, $c); }
if ( isset( $d ) ) { array_push($classargs, $d); }
$form->addElement( new $classname ( ...$classargs ) );
Either use the argument unpacking operator ...:
new $classname(...$classargs)
Or reflection:
(new ReflectionClass($classname))->newInstanceArgs($classargs)

How to streamline filtering in Mongo database

I am attempting to filter a report by using two parameters (name and id) in a Mongo DB. The way I have it set up now is this:
// if both params are empty
if ((empty($filterParams['name'])) && (empty($filterParams['id']))) {
$views = $mongoDb->selectCollection('views')->find([], []);
}
// if one of the two params are empty
if ((!empty($filterParams['name'])) || (!empty($filterParams['id']))) {
// name is empty
if ((empty($filterParams['name']))) {
$idQuery = array('id' => (int)$filterParams['id']);
$views = $mongoDb->selectCollection('views')->find($idQuery, []);
}
// id is empty
if ((empty($filteredParams['id']))) {
$nameQuery = array('name' => $filterParams['name']);
$views = $mongoDb->selectCollection('views')->find($nameQuery, []);
}
// neither are empty
if ((!empty($filterParams['name'])) && (!empty($filterParams['id']))) {
$fullQuery = array('id' => (int)$filterParams['id'], 'name' => $filteredParams['name']);
$views = $mongoDb->selectCollection('views')->find($fullQuery, []);
}
}
I was wondering if there was a way to streamline this so that the insertion can be done one time without the multiple if statements.
You are of course overengineering a very simple problem. All you really need to do here is take your "filterParams" ( which is likely from a request source ) and convert a possible "string" value for "id" into the the "numeric" format your database seems to be expecting ( going by the code written here ).
// $filterParams = array() // or it might be blank
$filterParams = array( "id" => "1", "something" => "else" );
$query = array();
foreach( $filterParams as $key => $value ) {
if ( $key == "id" )
$query[$key] = intval($value);
$query[$key] = $filterParams[$key];
}
$views = $mongoDb->selectCollection('views')->find($query);
So just start with an empty object and simply add in or tranform the keys are required for the query. If there are no keys at all, then this is an "empty" query object as MongoDB expects, with one key or both, then the same thing would also be passed through. All you really "need" here is to cast a "string" into the expected type for a given field.
In fact, especially since "filterParams" seems to be void of other data you could just alter the values in place rather than define another structure.
// $filterParams = array() // or it might be blank
$filterParams = array( "id" => "1", "something" => "else" );
$query = array();
foreach( $filterParams as $key => $value ) {
if ( $key == "id" )
$filterParams[$key] = intval($value);
}
$views = $mongoDb->selectCollection('views')->find($filterParams);
With most dynamic languages, MongoDB queries are really just data structures in the same format as what the language natively uses to express such things. So the basic principles apply to data structure manipulation anywhere.
Same JavaScript thing for example:
//var filterParams = {};
var filterParams = {
id: "1",
something: "here"
};
Object.keys(filterParams).forEach(function(key) {
if (key == "id" )
filterParams[key] = parseInt(filterParams[key]);
});
db.collection.find(filterParams);
Also "id" is typically an identifier used for a "unique" value to an object, just as _id is always used by MongoDB. So consider that when this key is present, then all other possible query arguments become redundant, unless you "really" want to check that the correct identifier was presented with the other correct properties of the object.

Best way to ensure a PHP variable is an array

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

PHP Session, why array is set to 1

I am creating a notification class which uses the session to store messages. I need to create them as a multidimensional array, so I can take advantage of different 'namespaces', so as to keep messages from displaying on the wrong pages.
Here is an example:
print_r($_SESSION)
Array
(
[EVENT_CMS] => Array
(
[Notifier] => Array
(
[0] => 'Your settings have been saved.'
[1] => 'You must re-upload...'
)
)
)
Now on the settings page, these messages will print with a call to the proper method.
I am having trouble setting up the message container within the class. This is what my constructor looks like:
public function __construct($namespace = 'Notifier') {
$this->_session_start();
if(defined('SESSION_NAMESPACE')){
$this->notifications =& $_SESSION[SESSION_NAMESPACE][$namespace];
} else {
$this->notifications =& $_SESSION[$namespace];
}
}
(The SESSION_NAMESPACE constant is defined, so the true block is executed.)
$Notify = new Notifier();
$Notify->add($_GET['test']);
print_r($_SESSION);
The above code yields me this array:
$_SESSION
Array
(
[EVENT_CMS] => Array
(
[Notifier] => 1
)
)
The add message method should update the session, right? Since the notifications array is a reference? The call to update_session() has no effect on the output...
public function add($message, $class = NULL) {
$message_node = $message;
$this->notifications[] = $message_node;
$this->update_session();
}
public function update_session(){
$this->SESSION[$this->namespace] &= $this->notifications;
}
You are mixing up the bitwise operator with the reference operator. The wrong one is used in your update_session() method.

Categories