In jQuery there is $.extend() function. That basically works like matching default settings with input settings. Well, I want to use similar technique in PHP, but it seems, that there is no similar function. So I'm wondering: Is there a similar function as .extend(), but in PHP? If not then, what alternatives are there?
This is an example class and how I get this effect currently. I also added a comment, how I would wish to do this:
class TestClass {
// These are the default settings:
var $settings = array(
'show' => 10,
'phrase' => 'Miley rocks!',
'by' => 'Kalle H. Väravas',
'version' => '1.0'
);
function __construct ($input_settings) {
// This is how I would wish to do this:
// $this->settings = extend($this->settings, $input_settings);
// This is what I want to get rid of:
$this->settings['show'] = empty($input_settings['show']) ? $this->settings['show'] : $input_settings['show'];
$this->settings['phrase'] = empty($input_settings['phrase']) ? $this->settings['phrase'] : $input_settings['phrase'];
$this->settings['by'] = empty($input_settings['by']) ? $this->settings['by'] : $input_settings['by'];
$this->settings['version'] = empty($input_settings['version']) ? $this->settings['version'] : $input_settings['version'];
// Obviously I cannot do anything neat or dynamical with $input_settings['totally_new'] :/
}
function Display () {
// For simplifying purposes, lets use Display and not print_r the settings from construct
return $this->settings;
}
}
$new_settings = array(
'show' => 30,
'by' => 'Your name',
'totally_new' => 'setting'
);
$TC = new TestClass($new_settings);
echo '<pre>'; print_r($TC->Display()); echo '</pre>';
If you notice, that there is a totally new setting: $new_settings['totally_new']. That should just get included inside the array as $this->settings['totally_new']. PS: Above code outputs this.
Try to use array_merge php function. Code:
array_merge($this->settings, $input_settings);
Related
From very long time i am working on php.
But one question may I have no idea about
like I have one function as bellow:
function hello($param1, $param2="2", $param3="3", $param4="4")
Now whenever I will use this function and if I need 4th params thats the $param4 then still I need to call all as blank like this one:
hello(1, '', '', "param4");
So is there any another way to just pass 1st and 4th param in call rather then long list of blanks ?
Or is there any other standard way for this ?
There was an RFC for this named skipparams but it was declined.
PHP has no syntactic sugar such as hello(1, , , "param4"); nor hello(1, default, default, "param4"); (per the RFC) for skipping optional parameters when calling a function.
If this is your own function then you can choose the common jQuery style of passing options into plug-ins like this:
function hello( $param1, $more_params = [] )
{
static $default_params = [
'param2' => '2',
'param3' => '3',
'param4' => '4'
];
$more_params = array_merge( $default_params, $more_params );
}
Now you can:
hello( 1, [ 'param4'=>'not 4, muahaha!' ] );
If your function requires some advanced stuff such as type hinting then instead of array_merge() you will need to manually loop $more_params and enforce the types.
One potential way you can do this, while a little bit hacky, may work well in some situations.
Instead of passing multiple variables, pass a single array variable, and inside the function check if the specific keys exist.
function hello($param1, $variables = ["param2" => "2", "param3" => "3", "param4" => "4"]) {
if(!array_key_exists("param2", $variables)) $variables['param2'] = "2";
if(!array_key_exists("param3", $variables)) $variables['param3'] = "3";
if(!array_key_exists("param4", $variables)) $variables['param4'] = "4";
echo "<pre>".print_r($variables, true)."</pre>";
}
This will allow you to set "param4" in the above variable, while still remaining default on all of the others.
Calling the function this way:
hello("test", ["param4" => "filling in variable 4"]);
Will result in the output being:
Array
(
[param4] => filling in variable 4
[param2] => 2
[param3] => 3
)
I don't generally recommend this if it can be avoided, but if you absolutely need this functionality, this may work for you.
The key here is that you have a specifically named index inside the array being passed, that you can check against inside the function itself.
The answer, as I see it, is yes and no.
No, because there's no way to do this in a standard fashion.
Yes, because you can hack around it. This is hacky, but it works ;)
Example:
function some_call($parm1, $parm2='', $parm3='', $parm4='') { ... }
and the sauce:
function some_call_4($parm1, $parm4) {
return some_call($parm1, '', '', $parm4);
}
So if you make that call ALOT and are tired of typing it out, you can just hack around it.
Sorry, that's all I've got for you.
It is an overhead, but you can use ReflectionFunction to create a class, instance of which that can be invoked with named parameters:
final class FunctionWithNamedParams
{
private $func;
public function __construct($func)
{
$this->func = $func;
}
public function __invoke($params = [])
{
return ($this->func)(...$this->resolveParams($params));
}
private function resolveParams($params)
{
$rf = new ReflectionFunction($this->func);
return array_reduce(
$rf->getParameters(),
function ($carry, $param) use ($params) {
if (isset($params[$param->getName()])) {
$carry[] = $params[$param->getName()];
} else if ($param->isDefaultValueAvailable()) {
$carry[] = $param->getDefaultValue();
} else {
throw new BadFunctionCallException;
}
return $carry;
},
[]
);
}
}
Then you can use it like this:
function hello($param1, $param2 = "2", $param3 = "3", $param4 = "4")
{
var_dump($param1, $param2, $param3, $param4);
}
$func = new FunctionWithNamedParams('hello');
$func(['param1' => '1', 'param4' => 'foo']);
Here is the demo.
How to correctly write default value for assocc array function argument?
function foo($arr['key']='value');
<?php
function foo($arr = null)
{
if (is_null($arr))
{
$arr = array(
'key' => 'value'
);
}
...
You cant use the direct way you tried above. Just work with this little workaround
Else you might go with this:
function foo($a = array('key' => 'value'))
{
...
But in my opinion its a bit unhandy to declare an array in the function head. Its purely on you how you want to use it
I share the PHP code base across all pages and for each HTTP request I dynamically require the "/configs/$site/config.php" file. The file looks like this:
<?php
$SiteConfiguration = [
'site_title => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
];
?>
The problem I'm facing is that I can't quite access this variable from functions.
For example:
function DisplayArticles() {
echo "Displaying ".$SiteConfiguration['articles_per_page'];
}
It will print just Displaying and not Displaying 10.
How can I fix this and have my $SiteConfiguration accessible everywhere? Should I use a class? What's the best practice here?
put
global $SiteConfiguration;
in your function, you can find some more info at http://www.php.net/manual/en/language.variables.scope.php
Since you asked for best practice info: (simplest form)
class MySite{
public static function getConfig(){
return array(
'site_title => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
);
}
}
then in your code you can recall it with
$config = MySite::getConfig();
and use it. (obviously with a better, more descriptive name than MySite ;) )
Advantages:
your php class autoloader will automagically load it for you if setup correctly and your classes can be found, this means not worrying wether you passed a variable, not worrying about function argument placement and not tainting your functions with unnecessary arguments that don't help describe what it does.
you control exactly the access to this data and can make it so that not even your own functions can accidentally change this data, not even when calling other functions that would also need access to it.
in my opinion it beats globals and it beats passing via arguments since it's cleaner and you control the access to it in all forms. You can make certain attributes readonly/writable via specific getter/setter options, keep count of how many times it's accessed and whatever else you can think of.
Here's another case where a class for configuration would work great:
class Config {
private static $site_config = array( 'h' => 'Hello', 'w' => 'World');
public static function get( $key) {
return isset( self::$site_config[$key]) ? self::$site_config[$key] : null;
}
}
echo Config::get( 'h') . ' ' . Config::get( 'w');
This will output: Hello World
use global keyword
function DisplayArticles() {
global $SiteConfiguration;
echo "Displaying ".$SiteConfiguration['articles_per_page'];
}
Edit
You should try to avoid global variable.
A better practice would be to pass your array in parameter
function DisplayArticles( array $config ) {
echo "Displaying ".$config['articles_per_page'];
}
$SiteConfiguration = array( 'site_title' => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
);
DisplayArticles( $SiteConfiguration );
You can try something like this.
Your "siteConfiguration.php" file:
<?php
$SiteConfiguration = [
'site_title' => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10
];
return $SiteConfiguration;
?>
And this function:
function getConfigVar($var) {
static $config = array();
if( empty($config) ) {
$config = require("siteConfiguration.php");
}
return array_key_exists($var, $config) ? $config[$var] : null;
}
This function can also be modified to handle several configs.
I've created several helper functions which I use when creating templates for Wordpress.
An example is this:
function the_related_image_scaled($w="150", $h="150", $cropratio="1:1", $alt="", $key="related" )
The problem is, if I only want to pass along the $alt parameter, I also have to populate $w, $h and $cropratio.
In one of my plugins, I use the following code:
function shortcode_display_event($attr) {
extract(shortcode_atts(array(
'type' => 'simple',
'parent_id' => '',
'category' => 'Default',
'count' => '10',
'link' => ''
), $attr));
$ec->displayCalendarList($data);
}
This allows me to call the function only using e.g.count=30.
How can I achieve the same thing in my own functions?
SOLUTION
Thanks to my name brother (steven_desu), I have come up with a solution that works.
I added a extra function (which I found on the net) to create value - pair from a string.
The code looks as follows:
// This turns the string 'intro=mini, read_more=false' into a value - pair array
function pairstr2Arr ($str, $separator='=', $delim=',') {
$elems = explode($delim, $str);
foreach( $elems as $elem => $val ) {
$val = trim($val);
$nameVal[] = explode($separator, $val);
$arr[trim(strtolower($nameVal[$elem][0]))] = trim($nameVal[$elem][1]);
}
return $arr;
}
function some_name($attr) {
$attr = pairstr2Arr($attr);
$result = array_merge(array(
'intro' => 'main',
'num_words' => '20',
'read_more' => 'true',
'link_text' => __('Read more')
), $attr);
extract($result);
// $intro will no longer contain'main' but will contain 'mini'
echo $intro;
}
some_name('intro=mini, read_more=false')
Info
With good feedback from Pekka, I googled and found some info regarding the Named Arguments and why it's not in PHP: http://www.seoegghead.com/software/php-parameter-skipping-and-named-parameters.seo
I would suggest using array_merge() and extract() at the beginning of your function, then passing parameters as arrays if this is a possibility.
function whatever($new_values){
$result = array_merge(array(
"var1" => "value1",
"var2" => "value2",
"var3" => "value3"
), $new_values);
extract($result);
echo "$var1, $var2, $var3";
}
whatever(array("var2"=>"new_value"));
The above will output:
value1, new_value, value3
it's a bit sloppy and uses more memory since it has to allocate the arrays, so it's the less efficient solution. But it does allow you to avoid redundancy. I'm sure a better method exists using magic meta-code, but I can't think of it off-hand.
Say this is your function:
function related_image_scaled($w="150", $h="150", $alt="", $key="related")
You can do this:
class ImageScaleParams {
public $w = 150;
public $h = 150;
public $cropratio = "1:1";
public $alt = "";
public $key = "related";
}
function related_image_scaled(ImageScaleParams $params) { ... }
Then call it like this:
$imgscale = new ImageScaleParams();
$imgscale.alt="New Alt";
related_image_scaled($imgscale);
You can also have various factories in ImageScaleParams such as:
class ImageScaleParams {
static function altFactory($alt) {
$imgscale = new ImageScaleParams();
$imgscale->alt = $alt;
return $imgscale;
}
}
Which you could call like this (equivalent to previous example):
related_image_scaled(ImageScaleParams::altFactory("New Alt"));
New Answer:
Could you not write the function using the extract function's default EXTR_OVERWRITE option?
function the_related_image_scaled($params) {
$w="150"; $h="150"; $cropratio="1:1"; $alt=""; $key="related";
extract($params);
//Do Stuff
}
Called with:
the_Related_image_scaled(array("alt"=>"Alt Text"));
You have the option of defaulting the parameters to null and only using them if they are not null:
function the_related_image_scaled($w=null, $h=null, $cropratio=null, $alt=null, $key = null) {
$output = //Get the base of the image tag
//including src leave a trailing space and don't close
if($w!==null) {
$output .= "width=\"$w\"";
}
//... Through all your parameters
return $output;
}
So only passing the alt parameter would look like:
echo the_related_image_scaled(null,null,null,$alt);
This question already has answers here:
Does PHP allow named parameters so that optional arguments can be omitted from function calls?
(17 answers)
Closed 1 year ago.
I have this:
function foo($a='apple', $b='brown', $c='Capulet') {
// do something
}
Is something like this possible:
foo('aardvark', <use the default, please>, 'Montague');
If it’s your function, you could use null as wildcard and set the default value later inside the function:
function foo($a=null, $b=null, $c=null) {
if (is_null($a)) {
$a = 'apple';
}
if (is_null($b)) {
$b = 'brown';
}
if (is_null($c)) {
$c = 'Capulet';
}
echo "$a, $b, $c";
}
Then you can skip them by using null:
foo('aardvark', null, 'Montague');
// output: "aarkvark, brown, Montague"
If it's your own function instead of one of PHP's core, you could do:
function foo($arguments = []) {
$defaults = [
'an_argument' => 'a value',
'another_argument' => 'another value',
'third_argument' => 'yet another value!',
];
$arguments = array_merge($defaults, $arguments);
// now, do stuff!
}
foo(['another_argument' => 'not the default value!']);
Found this, which is probably still correct:
http://www.webmasterworld.com/php/3758313.htm
Short answer: no.
Long answer: yes, in various kludgey ways that are outlined in the above.
You pretty much found the answer, but the academic/high-level approach is function currying which I honestly never found much of a use for, but is useful to know exists.
You can use some quirks, either passing all arguments as an array like ceejayoz suggests, or some overcomplicated code that parses func_get_args() and merges with a list of defaults. Not to copy-paste it, you'll have to use objects and traits. Finally, to be able to pass all kinds of values (not excluding null or false by making them a signal for default param substitution), you'll have to declare a dummy special type DefaultParam.
Another minus is that you have to duplicate the names and default values in the function declaration, if you want to get type hints or help in any IDE.
class DefaultParam {}
trait multi_arg_functions
{
private static function multi_arg($defaults, $list, $preserve_index = false)
{
$arg_keys = array_slice(array_keys($defaults), 0, count($list));
if ($preserve_index) {
$listed_arguments = array_slice($list, 0, count($arg_keys));
$extras = array_slice($list, count($arg_keys), null, true);
} else {
$listed_arguments = array_splice($list, 0, count($arg_keys));
$extras = &$list;
}
unset($list);
$arguments = array_combine($arg_keys, $listed_arguments);
$arguments = array_filter($arguments, function ($entry) {
return !($entry instanceof DefaultParam); //remove entries that mean default, a special class in this case
});
$arguments = array_merge($defaults, $arguments);
return [$arguments, $extras];
}
}
class b {
use multi_arg_functions;
static function func1($an_argument = 'a value', $another_argument = 'another value', $third_argument = 'yet another value') { //give defaults here to get hints in an IDE
list($args, $extras) = self::multi_arg( //note: duplicate names and defaults
[
'an_argument' => 'a value',
'another_argument' => 'another value',
'third_argument' => 'yet another value!',
], func_get_args());
echo json_encode(['args' => $args, 'extras' => $extras])."\n";
}
}
$default_param = new DefaultParam();
b::func1('value 1');
b::func1('value 2', $default_param, 'third argument');
b::func1('value 3', $default_param, 'third argument', 'fourth argument');
Note: by using preserve_index = true you get the extra arguments to start from their original index.
As of PHP 8, use named parameters:
function foo($a='apple', $b='brown', $c='Capulet') {
// do something
}
foo('apple', c:'Montague');
This let's you bypass as many parameters as you want, allowing them to take on their default value. This is helpful in long-winded functions like setcookie:
setcookie('macadamia', httponly:true); // skips over 5 parameters
Note that named parameters require all non-optional parameters to be passed. These may be passed positionally (as I've done here, no names on them) or with names in any order.