PHP If Shorthand Best Practce - php

Just a weird PHP question about best practice.
Assuming the following function:
function get_option($val) {
return false;
}
I want to assign to a $locale variable, the value returned from this function and, if false, set to a default en_GB one.
I discovered 2 option for achieving this goal:
1st Option:
$locale = ( $locale = get_option( 'language_code' ) ) ? $locale : 'en_GB';
2nd Option:
$locale = get_option( 'language_code' ) ? get_option( 'language_code' ) : 'en_GB';
I would like to know which one is more correct and why.
Thanks

The second option is better, but even better would be to use a shorthand ternary
$locale = get_option('language_code') ?: 'en_GB';
If your function returns only locale strings or false, this is the correct solution (and it doesn't require PHP7).
However, as mentioned in the comments, it might be an idea to return default values directly from the get_option function for a more architecturally sound solution. That means the caller is not made responsible for setting the default.
Just read that you're using Wordpress and have no control over the inner workings of the function, but the advice in general still stands

Both seem a bit verbose to me, to avoid duplicated calculations, I would prefer the first one (perhaps splitted to 2 lines of code).
You can create a helper function, this one has false hardcoded, but you could even pass it as a parameter:
function use_default_false($var, $default) {
return ($var !== false) ? $var : $default;
}
Then your code becomes:
$locale = use_default_false(get_option('language_code'), 'GB');
Since PHP5.3 you can use the shorthand ternary operator ?:.
Be aware that it will check the left-hand side argument for truthy, which prevents it to be used if the valid value you're checking evaluates to false (e.g.: 0, "", "0", array()...). Because of that, I wouldn't generally recommend it, but in this case I assume the locale is a non-empty non-"0" string, so it should be fine.
$locale = get_option('language_code') ?: 'GB';
With PHP7 you can use the null coalesce operator ??.
It checks for NULL so you have to alter the default value returned by your function.
$locale = get_option('language_code') ?? 'GB';

I would prefer the second one
basically if get_option( 'language_code' ) returns true then get_option( 'language_code' ) execute else other option.
it is easier to understand, and maintainable.
for duplicate code issue use something similer to this:
you need to post some more code, but here is a better way to do it:
var var1 = null;
function get_option( somevar ){
if (var1 != null) {
return true;
} else {
var1 = do some stuff;
return true;
}
}
and then call the function like this
$locale = get_option( 'language_code' ) ? var1 : 'en_GB';

Related

PHP multiple url's check and do action

Can't figure out how to check multiple URL's and stop loading selected plugin. With other plugins it's all good when need to check only one page, but when it comes to multiple pages - ain't working at all.
Code:
<?php
$request_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$is_admin = strpos( $request_uri, '/wp-admin/' );
if( false === $is_admin ){
add_filter( 'option_active_plugins', function( $plugins ){
global $request_uri;
$berocket_filter_plugin = "woocommerce-ajax-filters/woocommerce-filters.php";
$c = array_search( $berocket_filter_plugin, $plugins );
if( false !== $c && in_array( strpos ($request_uri, ['/shop/','/product-category/'], true ) )){
unset( $plugins[$c] );
}
return $plugins;
} );
}
I'm trying to check is page url is exactly 'shop' page slug, or contains '/product-category/' slug, as soon as I'm trying to use this array, it's not doing his work at all.
Any suggestions are appreciated!
To be honest I'm not a wordpress expert (I hate the bloody thing) but...
The strpos function second argument needs to be a string, you're passing in an array that evaluates to 0 I believe. And the 3rd argument is an offset on the sting, since you set it to true i believe that would evaluate to 1. So... what happens is:
in_array(strpos('myurl',0,1))
strpos('myurl',0,1) = 0 // as we're looking for a '0' in 'yurl'
consider working with the url as a dictionary. Instead of keeping it as a string run a no_empty explode to get something like:
['shop' => true,
'product-category' => true]
That way instead of running an array_search or strpos for each check you can simply invoke something along the lines of:
if(!isset($request_uri['wp-admin']) {...
if (
isset($request_uri['product-category']) ||
(count($request_uri) === 1 && isset($request_uri['shop']))
) {...}
}
There is a neater way to write this but this should be simple to read and understand the logic.
Similar approach goes for the plugins array. The only difference between [$id => $value] and [$value => $id] is the fact that you need to run a search on the array and check if it's set instead of simply checking if it's set.

How can I safely retrieve this property of this object?

I have a Stripe subscription object that looks like this...
subscription: {
items: {
data: [
plan: {
id: 'my_plan_id'
}
]
}
}
What's the best way to safely retrieve the plan id? Currently I am doing the following.
'plan_id' => $subscription->items->data[0]->plan->id,
But, it looks like that will fail if items, data[0], or plan, is not set. I could do nest if statements like, if (isset($subscription->items) && isset(data[0]) ..., but I am not sure that is the best way.
Is there a PHP method or Laravel method that I can use to extract that property safely that would be cleaner than that?
If you're using PHP 7+, you can use the null coalesce operator:
'plan_id' => $subscription->items->data[0]->plan->id ?? $default,
This will evaluate the value if it's available, otherwise it will use the default, without generating any warnings or errors.
Example:
$foo = new stdClass();
var_dump($foo->bar->baz->data[0]->plan->id ?? null);
Output:
NULL
You can use an isset function over the entire selector:
isset($subscription->items->data[0]->plan->id) ? $subscription->items->data[0]->plan->id : null;
in PHP version 8
you should use Nullsafe operator as follow:
'plan_id' => $subscription?->items?->data[0]?->plan->id,
A rather cumbersome but generic method to access nested structures by a list of keys; can be made into a reusable function easily:
$id = array_reduce(['items', 'data', 0, 'plan', 'id'], function ($o, $k) {
if (!$o) {
return null;
} else if (is_array($o) && isset($o[$k])) {
return $o[$k];
} else if (isset($o->$k)) {
return $o->$k;
}
}, $subscription);
you can take help of ternary condition to check if the value is set or not. If set then put that value otherwise put default as
$id=isset($subscription->items->data[0]->plan->id) ? $subscription->items->data[0]->plan->id : $your_dafault_value;
If the value is set then the $id looks like
$id='my_plan_id';//In most cases we should consider null as a default value
If the value is not set then the $id looks liks
$id='your_dafault_value';

Is there a function to check if an array key exists, and get the value if it does, in one call?

I want to avoid scanning the array twice. Something like TryGetValue in C#.
No, there isn't a built-in function that does what you want. However, it's not hard to write a new one:
function tryGetValue($array, $key) {
return (array_key_exists($key, $array)) ? $array[$key] : NULL;
}
Example usage:
$array = array('foo' => 'bar', 'baz', 'bak', 'bam');
var_dump(tryGetValue($array, 'foo')); // string(3) "bar"
var_dump(tryGetValue($array, 's')); // NULL
var_dump(tryGetValue($array, 2)); // string(3) "bam"
var_dump(tryGetValue($array, 4)); // NULL
UPDATE
This is not recommended practice. Because # suppresses all exceptions in that expression.
However, AFAIK, it is the only efficient way to meet OP's stated criteria (see comment on question):
I want to avoid errors/warnings and I want to avoid scanning the array twice.
Recommended practice can be seen in Amit's answer.
That is, don't "micro-optimize" your code. Don't be so concerned about "scanning the array twice". Write clean, robust code. Later, if testing finds a performance problem, evaluate then how to improve performance.
Do handle errors and unexpected conditions (exceptions).
ORIGINAL ANSWER
#jszobody gave an answer in a comment on the question. Expanding on his comment:
What do you want to return, if the array key doesn't exist?
If null, then one approach is "just do it":
#$array[$key]
("#" suppresses warning.)
If something else, and you don't store null values in array - so you don't ever want null to be returned - then this code becomes:
#$array[$key] ?? $YourDefaultValue
Unless your design BOTH 1) allows storing of null values in the array, AND 2) wants a default other than null. If both of these conditions are true, then you would need to explicitly test whether the key exists, using array_key_exists.
Check these microbenchmarks I created on 3v4l.
Here are the functions.
function tryGetValue1( $array, $key, Closure $default ) {
return array_key_exists($key, $array)
? $array[$key] : $default();
}
function tryGetValue2( $array, $key, Closure $default ) {
return ($value = #$array[$key]) !== null || array_key_exists($key, $array)
? $value : $default();
}
function tryGetValue3( $array, $key, Closure $default ) {
return isset($array[$key]) || array_key_exists($key, $array)
? $array[$key] : $default();
}
function tryGetValueNotNull1( $array, $key, Closure $default ) {
return ($value = #$array[$key]) !== null
? $value : $default();
}
function tryGetValueNotNull2( $array, $key, Closure $default ) {
return isset($array[$key])
? $array[$key] : $default();
}
It depends on if you care about null, and what version of PHP you're using.
Looks like the compilers try to optimize toward the isset and array_key_exists operations, at least for small arrays. This might imply that arrays have their own internal iterator state for this and similar purposes.
Interestingly, you can combine isset and array_key_exists (see tryGetValue3) and come out ahead sometimes.
This isn't a fully appropriate benchmark, as there are no null existing keys defined in the test array. Feel free to expand on the example. :)

PHP - Inherit function default value

I have a function that look like this:
function test($arg1 = 'my_value', $arg2 = 'second')
{
}
When I call it I only want to set the second value to something different, like this:
test(inherit, 'changed value');
I found out that it is possible to add this line to my function (when my "inherit" is changed to null):
$arg1 = ( is_null( $arg1 ) ? 'my_value' : $arg1 );
Is there a better way, a nicer way to solve it?
Depending on the nature and number of your parameters it may be reasonable to use named parameters (at least emulated):
function xyz($args) {
$args += array(
'x' => 'default',
'q' => 'default 2',
// ...
);
// ...
}
xyz(array('q' => 'hehe, not default'));
The way you have solved it is actually pretty usable.
The other way is to pass the same value as the default value every time on the function call.
If that is structural, then you have to reconsider the function.
Make two different functions:
// Full function
function testex($arg1 = 'my_value', $arg2 = 'second')
{
}
// Shorthand when just argument 2 is needed
function test2($arg2 = 'second')
{
return testex('my_value', $arg2);
}
That way, you don't have to pass null to the first parameter when you don't need to.
You will have to flip them, you can't leave the first value to be blank,
Set the first value and let the second one be the default value;
UPDATE:
If you want to have dynamic length to your argument consider using the func_get_args();
function something() {
$args = func_get_args();
....
}
then you can test your arguments for different value or datatype to make them do whatever yuo please

PHP Function with Optional Parameters

I've written a PHP function that can accept 10 parameters, but only 2 are required. Sometimes, I want to define the eighth parameter, but I don't want to type in empty strings for each of the parameters until I reach the eighth.
One idea I had was to pass an abstracted function with an array of parameters which passes it along to the real function.
Is there a better way to set up the function so I can pass in only the parameters I want?
What I have done in this case is pass an array, where the key is the parameter name, and the value is the value.
$optional = array(
"param" => $param1,
"param2" => $param2
);
function func($required, $requiredTwo, $optional) {
if(isset($optional["param2"])) {
doWork();
}
}
Make the function take one parameter: an array. Pass in the actual parameters as values in the array.
Edit: the link in Pekka's comment just about sums it up.
To accomplish what you want, use an array Like Rabbot said (though this can become a pain to document/maintain if used excessively). Or just use the traditional optional args.
//My function with tons of optional params
function my_func($req_a, $req_b, $opt_a = NULL, $opt_b = NULL, $opt_c = NULL)
{
//Do stuff
}
my_func('Hi', 'World', null, null, 'Red');
However, I usually find that when I start writing a function/method with that many arguments - more often than not it is a code smell, and can be re-factored/abstracted into something much cleaner.
//Specialization of my_func - assuming my_func itself cannot be refactored
function my_color_func($reg_a, $reg_b, $opt = 'Red')
{
return my_func($reg_a, $reg_b, null, null, $opt);
}
my_color_func('Hi', 'World');
my_color_func('Hello', 'Universe', 'Green');
You can just set the default value to null.
<?php
function functionName($value, $value2 = null) {
// do stuff
}
In PHP 5.6 and later, argument lists may include the ... token to denote that the function accepts a variable number of arguments. The arguments will be passed into the given variable as an array; for example:
Example Using ... to access variable arguments
<?php
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
?>
The above example will output:
10
Variable-length argument lists PHP Documentation
NOTE: This is an old answer, for PHP 5.5 and below. PHP 5.6+ supports default arguments
In PHP 5.5 and below, you can achieve this by using one of these 2 methods:
using the func_num_args() and func_get_arg() functions;
using NULL arguments;
How to use
function method_1()
{
$arg1 = (func_num_args() >= 1)? func_get_arg(0): "default_value_for_arg1";
$arg2 = (func_num_args() >= 2)? func_get_arg(1): "default_value_for_arg2";
}
function method_2($arg1 = null, $arg2 = null)
{
$arg1 = $arg1? $arg1: "default_value_for_arg1";
$arg2 = $arg2? $arg2: "default_value_for_arg2";
}
I prefer the second method because it's clean and easy to understand, but sometimes you may need the first method.
Starting with PHP 8 you are able to use named arguments:
function namedParameters($paramOne, $paramTwo, $paramThree = 'test', $paramFour = null)
{
dd($paramOne, $paramTwo, $paramThree, $paramFour);
}
We can now call this function with the required params and only the optinal params, that we want to differ from the default value which we specified in the function.
namedParameters('one', 'two', paramFour: 'four');
Result:
// "one", "two", "test", "four"
I think, you can use objects as params-transportes, too.
$myParam = new stdClass();
$myParam->optParam2 = 'something';
$myParam->optParam8 = 3;
theFunction($myParam);
function theFunction($fparam){
return "I got ".$fparam->optParam8." of ".$fparam->optParam2." received!";
}
Of course, you have to set default values for "optParam8" and "optParam2" in this function, in other case you will get "Notice: Undefined property: stdClass::$optParam2"
If using arrays as function parameters, I like this way to set default values:
function theFunction($fparam){
$default = array(
'opt1' => 'nothing',
'opt2' => 1
);
if(is_array($fparam)){
$fparam = array_merge($default, $fparam);
}else{
$fparam = $default;
}
//now, the default values are overwritten by these passed by $fparam
return "I received ".$fparam['opt1']." and ".$fparam['opt2']."!";
}
If only two values are required to create the object with a valid state, you could simply remove all the other optional arguments and provide setters for them (unless you dont want them to changed at runtime). Then just instantiate the object with the two required arguments and set the others as needed through the setter.
Further reading
Martin Fowler on Constructor vs Setter Injection and
Dependency injection through constructors or property setters?
I know this is an old post, but i was having a problem like the OP and this is what i came up with.
Example of array you could pass. You could re order this if a particular order was required, but for this question this will do what is asked.
$argument_set = array (8 => 'lots', 5 => 'of', 1 => 'data', 2 => 'here');
This is manageable, easy to read and the data extraction points can be added and removed at a moments notice anywhere in coding and still avoid a massive rewrite. I used integer keys to tally with the OP original question but string keys could be used just as easily. In fact for readability I would advise it.
Stick this in an external file for ease
function unknown_number_arguments($argument_set) {
foreach ($argument_set as $key => $value) {
# create a switch with all the cases you need. as you loop the array
# keys only your submitted $keys values will be found with the switch.
switch ($key) {
case 1:
# do stuff with $value
break;
case 2:
# do stuff with $value;
break;
case 3:
# key 3 omitted, this wont execute
break;
case 5:
# do stuff with $value;
break;
case 8:
# do stuff with $value;
break;
default:
# no match from the array, do error logging?
break;
}
}
return;
}
put this at the start if the file.
$argument_set = array();
Just use these to assign the next piece of data use numbering/naming according to where the data is coming from.
$argument_set[1][] = $some_variable;
And finally pass the array
unknown_number_arguments($argument_set);
function yourFunction($var1, $var2, $optional = Null){
... code
}
You can make a regular function and then add your optional variables by giving them a default Null value.
A Null is still a value, if you don't call the function with a value for that variable, it won't be empty so no error.
As of PHP 7.1.0, type declarations can be marked nullable by prefixing the type name with a question mark (?). This signifies that the value can be of the specified type or null
<?php
function name(?string $varname){
echo is_null($varname);
}
name();
name('hey');
?>
for more info: Click here
If you are commonly just passing in the 8th value, you can reorder your parameters so it is first. You only need to specify parameters up until the last one you want to set.
If you are using different values, you have 2 options.
One would be to create a set of wrapper functions that take different parameters and set the defaults on the others. This is useful if you only use a few combinations, but can get very messy quickly.
The other option is to pass an array where the keys are the names of the parameters. You can then just check if there is a value in the array with a key, and if not use the default. But again, this can get messy and add a lot of extra code if you have a lot of parameters.
PHP allows default arguments (link). In your case, you could define all the parameters from 3 to 8 as NULL or as an empty string "" depending on your function code. In this way, you can call the function only using the first two parameters.
For example:
<?php
function yourFunction($arg1, $arg2, $arg3=NULL, $arg4=NULL, $arg5=NULL, $arg6=NULL, $arg7=NULL, $arg8=NULL){
echo $arg1;
echo $arg2;
if(isset($arg3)){echo $arg3;}
# other similar statements for $arg4, ...., $arg5
if(isset($arg8)){echo $arg8;}
}
Just set Null to ignore parameters that you don't want to use and then set the parameter needed according to the position.
function myFunc($p1,$p2,$p3=Null,$p4=Null,$p5=Null,$p6=Null,$p7=Null,$p8=Null){
for ($i=1; $i<9; $i++){
$varName = "p$i";
if (isset($$varName)){
echo $varName." = ".$$varName."<br>\n";
}
}
}
myFunc( "1", "2", Null, Null, Null, Null, Null, "eight" );
func( "1", "2", default, default, default, default, default, "eight" );

Categories