It is common in php to store config data in arrays, and that is read only. Has someone made utility to make possible to save changes to array direct in source? I don't mean serializing array to some file but update value in source where it was read.
This is how I manipulate and save the state of config arrays.
Here I create a config array (but let's assume you already have one in the same format), I write it. Then next I read it in, make an edit and save back to the file.
Adjust to your liking.
<?php
function write_config($config, $path) {
file_put_contents($path, '<?php return ' . var_export($config, true). ';');
}
function read_config($path)
{
return include $path;
}
$config_path = '/tmp/config.php';
$config = array(
'foo' => 'bar'
);
write_config($config, $config_path);
$config = read_config($config_path);
var_dump($config);
$config['foo'] = 'baz';
write_config($config, $config_path);
$config = read_config($config_path);
var_dump($config);
Output:
array (size=1)
'foo' => string 'bar' (length=3)
array (size=1)
'foo' => string 'baz' (length=3)
I usually have a base array set with defaults, and have another array for adjustments that I use to override the first. I use array_merge_recursive to apply the latter over the first.
Related
In CoffeeScript, Clojure, ES6 and many other languages we have destructuring of objects/maps/etc somewhat like this:
obj = {keyA: 'Hello from A', keyB: 'Hello from B'}
{keyA, keyB} = obj
I've found the list function in php which lets you destructure arrays like so:
$info = array('coffee', 'brown', 'caffeine');
list($drink, $color, $power) = $info;
Is there a way to destructure objects or associative arrays in PHP? If not in the core libs maybe someone wrote some smart helper function?
For PHP 7.0 and below that is beyond the functionality of list. The docs state:
list only works on numerical arrays and assumes the numerical indices start at 0.
One of the things that could suit your purpose would be the extract() function which imports variables from an array into the current symbol table. While with list you are able to define variable names explicitly, extract() does not give you this freedom.
Extracting an associative array
With extract you could do something like that:
<?php
$info = [ 'drink' => 'coffee', 'color' => 'brown', 'power' => 'caffeine' ];
extract($info);
var_dump($drink); // string(6) "coffee"
var_dump($color); // string(5) "brown"
var_dump($power); // string(8) "caffeine"
Extracting an Object
Extracting an object works almost the same. Since extract only takes an array as an argument we need to get the objects properties as an array. get_object_vars does that for you. It returns an associative array with all public properties as key and their values as value.
<?php
class User {
public $name = 'Thomas';
}
$user = new User();
extract( get_object_vars($user) );
var_dump($name); // string(6) "Thomas"
Pitfalls
extract() is not the same as list since it does not allow you to explicitly define the variable names that get exported to the symbol table. The variable names correspond the array keys by default.
list is a language construct while extract() is a function
It might happen that you overwrite variables that you have defined beforehand unintentionally
Your array keys might be invalid as variable names
With the $flags parameter that you can pass as second argument to extract() you can influence the behavior in case of colliding or invalid variables. But still it's important to know how extract() works and to use it with cauton.
Edit: As of PHP 7.1 this is possible:
http://php.net/manual/en/migration71.new-features.php#migration71.new-features.support-for-keys-in-list
You can now specify keys in list(), or its new shorthand [] syntax. This enables destructuring of arrays with non-integer or non-sequential keys.
https://php.net/manual/en/migration71.new-features.php#migration71.new-features.symmetric-array-destructuring
The shorthand array syntax ([]) may now be used to destructure arrays for assignments (including within foreach), as an alternative to the existing list() syntax, which is still supported.
For example this:
$test_arr = ['a' => 1, 'b' => 2];
list('a' => $a, 'b' => $b) = $test_arr;
var_dump($a);
var_dump($b);
Will output the following as of 7.1.0
int(1)
int(2)
I noticed the accepted answer missed out examples that use the short-hand notation, security issues with using extract, and IDE issues.
Numerical Array Destructuring (PHP 7.1)
As of PHP 7.1 numerical array destructuring (Symetric array destructuring) is supported like so:
<?php
$data = [55, 'John', 'UK'];
[$id, $name] = $data; // short-hand (recommended)
list($id, $name) = $data; // long-hand
Notice that you can miss items out if you don't want them.
Associative Array Destructuring (PHP 7.1)
You can also destructure associative arrays (Support for keys in list) like so:
<?php
$data = ['id' => 55, 'firstName' => 'John', 'country' => 'UK']
['id' => $id, 'firstName' => $name] = $data; // short-hand (recommended)
list('id' => $id, 'firstName' => $name) = $data; // long-hand
Notice that you can miss items out if you don't want them. Also the variable name can be different to the property name.
Object Destructuring (PHP 7.1)
Unfortunately there is no object destructuring. However you can convert an object to an associative array using get_object_vars, and then use associative array destructuring.
<?php
class User {
public $id;
public $name;
public $country;
}
$user = new User();
$user->id = 55;
$user->name = 'John';
$user->country = 'UK';
['id' => $id, 'firstName' => $name] = get_object_vars($user)
However, this can break some IDE features. These are some issues I noticed when using PHPStorm 2019.1:
IDE's may no longer understand the type for the variables, so you would need to add some #var Type PHPDocs to maintain auto-complete functionality
Does not work well with refactoring tools. For example, if you rename one of the properties, the array destructuring portion will not also automatically rename.
So I recommend just doing it the normal way:
$id = $user->id
$name = $user->firstName
Do NOT use extract
With extract, all variables are always set. There it is a really bad idea to use it because:
It can lead to security issues. Even if your careful, it can lead to non-obvious security holes in the future. If you do use it, don't use it with user input (e.g. $_GET, $_POST), unless you want to make a malicious hacker's day.
Can lead to hard to detect bugs
If the class or array changes in the future, by introducing new properties, it can break your code if it coincides with an already used variable, unless you use the EXTR_SKIP flag or similar
Variable variables are one way to achieve this:
$args = ['a' => 1, 'b' => 2, 'c' => 3];
foreach (['a', 'c'] as $v) $$v = $args[$v];
// $a is 1, $b is undefined, $c is 3
It's really not pretty, and thankfully this has been addressed in 7.1 by https://wiki.php.net/rfc/short_list_syntax . This would let you say ['a' => $a, 'c' => $c] = $args; in the above example.
Since 7.1 includes a way to use a different name for your var than the assoc array key. This is pretty straight-forward using variable variables here too:
foreach (['a' => 'eh', 'b' => 'bee'] as $k => $v) $$v = $args[$k];
// $eh is 1, $bee is 2
Some developers, and some coding styles, define $$var as an anti-pattern similar to using eval, extract, and the GPR magic variables directly. This is because using variable variables makes code harder to understand, which leads directly to bugs and prevents static code analysis tools from functioning.
If you do adopt $$var, it can be helpful to use the ${$var} form instead, which makes it obvious that the author didn't simply type one too many $'s, and may spare the author immediate negative feedback when their code is audited.
One simple solution is to read the Object as an Array. So assuming you use #Yahya Uddin's User object, you can do like so:
['id' => $id, 'firstName' => $name] = (array)$user
// $id = 55, name = 'john'
This will tell PHP to read this objective as an associative Array.
Could someone explain me, why this code works properly without crashing initial array structure?
function setArrayValueByPath($path, $value, &$array)
{
foreach ($path as $p) {
$array = &$array[$p];
}
$array = $value;
return true;
}
$array = [
'a' => 'v1',
'b' => 'v2',
];
setArrayValueByPath(['hello', 'world'], '!!!', $array);
echo '<pre>';
print_r($array);
echo '</pre>';
When I run the code, I see:
Array
(
[a] => v1
[b] => v2
[hello] => Array
(
[world] => !!!
)
)
Due to the line in function:
$array = $value;
it should replace $array value, but it does not happen.
My function is based on code snippets are given here: Using a string path to set nested array data
Thank you.
Let's examine this one step at a time.
The parameter $array is a local variable within the function which contains a reference to some external array being passed in.
foreach ($path as $p) {
This iterates over ['hello', 'world']
$array = &$array[$p];
Take the original array, and "index" it with $p (i.e. [hello]). This does not currently exist so it is added to the original array. Then take a reference to that new member and save it in the local variable $array. I.e. you just created a new member of the original array, and the local variable $array no longer points to the original external array.
On the second iteration, take the variable currently pointed to by $array (see just above) and index it with $p (world). This does not exist, so create it.
}
At this point $array points to the member {original array}[hello][world]. I use the syntax {original array} here because you no longer have a reference to it, only a reference to an array two levels nested within it.
$array = $value;
This sets the value of that member to !!!, giving exactly the data structure you see.
I need a regex to search in an array and then return the value of that key.
for example:
I want to get the value of the var1 key.
my config
<?php
return [
'var1' => 'test1',
'var2' => 'test2',
'var3' => 'test3'
];
?>
then it should return test1
This is an unorthodox question, but there's a straightforward enough answer here that does not require a regex, so I will present this instead.
Assume that your config file is called config.php and contains the code snippet that you provided in your example:
<?php
return [
'var1' => 'test1',
'var2' => 'test2',
'var3' => 'test3',
];
You can actually assign the return value of include or require to a variable. For example, in another script (assuming you are in the same directory) you can do this:
<?php
// your config file...
$file = __DIR__ . '/config.php';
// ensure the file exists and is readable
if (!is_file($file) || !is_readable($file)) {
throw new RuntimeException(
sprintf('File %s does not exist or is not readable!', $file)
);
}
// include file and assign to variable `$config`
// which now contains the returned array
$config = include $file;
echo 'original config:' . PHP_EOL;
print_r($config);
// update config key `var1`
$config['var1'] = 'UPDATED!';
echo 'updated config:' . PHP_EOL;
print_r($config);
This yields:
original config:
Array
(
[var1] => test1
[var2] => test2
[var3] => test3
)
updated config:
Array
(
[var1] => UPDATED!
[var2] => test2
[var3] => test3
)
I was originally a little surprised that you could use return outside of the context of a function/method, but it's perfectly valid. You learn something new every day... This use case is actually documented under the documentation for include - refer to Example #5 include and the return statement for more details.
Please note, that if you are using include or require to pull in untrusted or external scripts, the usual security considerations apply. This is discussed in the documentation linked above.
Also, if your included file contains a syntax error, then you will probably get a parse error or similar, but I guess that's an obvious point!
Edit
Finally, I should point out that your question does not ask how to save the updated configuration back into the file, but you did leave a comment underneath that suggests that you want to do this also.
However, if you want to update and persist a configuration on file, I would definitely using a more malleable method to write/read this data to/from disk - perhaps json_encode() and json_decode, serialize() and unserialize().
Nevertheless, here's a naive solution in which you can write your updated config:
// ...
// file has to be writeable!
if (!is_writeable($file)) {
throw new RuntimeException(
sprintf('File %s is not writeable!', $file)
);
}
file_put_contents($file,
sprintf('<?php return %s;', var_export($config, true)));
Further reading:
is_file()
is_readable()
is_writeable()
file_put_contents()
var_export()
Hope this helps :)
I have a PHP file a configuration file coming from a Yii message translation file which contains this:
<?php
return array(
'key' => 'value'
'key2' => 'value'
);
?>
I want to load this array from another file and store it in a variable
I tried to do this but it doesn't work
function fetchArray($in)
{
include("$in");
}
$in is the filename of the PHP file
Any thoughts how to do this?
When an included file returns something, you may simply assign it to a variable
$myArray = include $in;
See http://php.net/manual/function.include.php#example-126
Returning values from an include file
We use this in our CMS.
You are close, you just need to return the value from that function.
function fetchArray($in)
{
if(is_file($in))
return include $in;
return false
}
See example 5# here
As the file returning an array, you can simply assign it into a variable
Here is the example
$MyArray = include($in);
print_r($MyArray);
Output:
Array
(
[key] => value
[key2] => value
)
In PHP I often do the following:
$_SESSION['var']['foo'] = array('bar1' => 1, 'bar2' => 2);
// ...
$_SESSION['var']['foo']['bar2'] = 3;
// ...
echo $_SESSION['var']['foo']['bar2']; // 3
I'm wondering what the recommended way of storing multidimensional arrays in a session with Kohana.
I know I can do the following, but I don't know how to make it work with multidimensional, specifically the get portion:
Session::instance()->set('var', array(
'foo' => array(
'bar1' => 1,
'bar2' => 2,
),
));
// ...
// how do I set just bar2?
// ...
// this gets the whole array, but how do I get just bar2?
Session::instance()->get('var');
So, the questions are:
How do I set just bar2?
How do I get just bar2?
Is there a way to do either of these in Kohana 3?
I'd love to use the native sessions, but we are trying to use database sessions.
The short answer is that there is no way to do that, given the current implementation of Kohana sessions. You have two alternatives:
Either get and set the entire array, editing the bits you need each time:
$array = Session::instance()->get('var');
$array['foo']['bar2'] = 'baz';
Session::instance()->set('var', $array);
Or you override the Kohana_Session->get() and ->set() methods (which are defined here on github).
Keep in mind that, given the wonderful "layered" filesystem in Kohana, you can actually extend the class, modifying just the method you need, without editing the core Kohana code.
My idea would be to change the $key parameter to accept either strings or arrays. If you pass in an array, it should interpret each element in the array as a "deeper" level.
$key = array('var', 'foo', 'bar2');
Session::instance()->get($key, $default);
Session::instance()->set($key, 'baz');
$session = & Session::instance()->as_array();
$session['foo']['bar2'] = 'baz';
UPD. Also, you can use Arr::path():
$bar2 = arr::path(Session::instance()->as_array(), 'foo.bar2');// returns 'baz'
$bars = arr::path(Session::instance()->as_array(), '*.bar2'); // returns array of bar2's