I am trying here to set the vale of the first child element of $map. I need to do this by referencing the path of the array i.e [0]['child'].
The function returns the path value ok if set but I am having trouble changing the value of that of that element. So in this case I want $map[0]['child'] to equal "new".
function getArrayPath($arr,$path) {
foreach($path as $item){
$arr = $arr[$item];
}
return $arr;
}
$map='[{"child":""},{"child":""},{"child":""}]';
$map=json_decode($map,true);
$path = array("0","child");
$target = getArrayPath($map,$path);
if($target==""){
$target="new";
}
var_dump($map);
You can solve this by using references:
function &getArrayPath(&$arr,$path) {
// ^--return ^--receive
// reference reference
foreach($path as $item){
$arr =& $arr[$item];
// ^--assign reference
}
return $arr;
}
$map = json_decode('[{"child":"1"},{"child":"2"},{"child":"3"}]', true);
$path = array("0","child");
$target =& getArrayPath($map,$path);
// ^--assign reference
as you can see in this demo (your code slightly changed).
Since PHP does not assign/return by reference as default, you always need to explicitly do that by adding the & operator.
Related
Suppose the following piece of code:
<?php
declare (strict_types = 1);
define('SOURCE_PATH', 'C:\xampp\htdocs\atlantis\ProductImages\\');
$dest = explode('\\', SOURCE_PATH);
$dest[count($dest) - 2] = 'ToUpload';
$dest = implode('\\', $dest);
function transfer(string $file)
{
global $dest;
if (!#chdir($dest)) {
mkdir($dest);
chdir($dest);
}
// ...
}
function compress(string $path)
{
global $dest;
$zip = new ZipArchive();
$zip->open(dirname($dest, 1) . '\\' . explode('\\', SOURCE_PATH)[count(explode('\\', SOURCE_PATH)) - 2] . '.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// ...
}
So, intelephense on VS Code keeps complaining about Expected type 'string'. Found 'string[]'. on all four instances where I use $dest in my functions...
What's that intelephense doesn't like about my code? Note that page runs fine... And what's that string[] type? implode() returns a string, so what's the problem here?
EDIT: It's most probably a bug of intelephense...
I calculated $dest like this:
$dest = dirname(SOURCE_PATH, 1) . '\ToUpload\\';
which doesn't involve explode()ing, and indeed the error is gone... So somehow intelephense fails to take into consideration that implode() returns a string and not an array...
Actually, I filed a bug report on github and I'm awaiting Mewburn's response on this... :)
Global variables can be a little bit wonky in intelephense, an issue already exists to improve the support. In this specific case, the type that is inferred is determined by the first assignment. Since that is the explode statement, it'll assume $dest is an array of strings.
There are a couple of solutions to this:
Make sure to intelephense what the type of $dest is with comments.
function transfer(string $file)
{
/** #var string $dest */
global $dest;
}
Make sure the first assignment of $dest actually is a string:
$tempdest = explode('\\', SOURCE_PATH);
$tempdest[count($tempdest) - 2] = 'ToUpload';
$dest = implode('\\', $dest);
I think, it's because the implode parameters have been changed.
https://www.php.net/manual/de/migration74.deprecated.php
says the following:
Implode with historical parameter order Passing parameters to implode() in reverse order is deprecated, use implode($glue, $parts)
instead of implode($parts, $glue).
I have an XML file and a variable. I want to write a function to replace a specific node's value. Example:
write_xml("->system_settings->settings->hostname",'Helloooooooo');
function write_xml($tag_address, $value) {
$xml = simplexml_load_file("test.xml")
or die("Error: Cannot create object");
$xml->system_settings->settings->hostname = $value;
$xml->asXML("test.xml");
}
In this example, the node ->system_settings->settings if helloooooo should be replaced with hostname.
My problem is: in the above code, I entered the path manually and it works. But if I assign my path dynamically (like below), it doesn't work:
write_xml("->system_settings->settings->hostname",'Helloooooooo');
function write_xml($tag_address, $value) {
...
$xml->$tag_address = $value; // <---- doesn't work
$xml->asXML("test.xml");
}
What should i do?
So I'm using the following code to pull a random file from a folder and I would like to make it so there is never a chance of pulling up the current file again (ie: seeing the same image/document twice in a row).
How would I go about this? Thanks in advance!
function random_file($dir = 'destinations')
{
$files = glob($dir . '/*.*');
$file = array_rand($files);
return $files[$file];
}
Store the last viewed filename in a cookie or in the session.
Here's how to do it with a cookie:
function random_file($dir = 'destinations') {
$files = glob($dir . '/*.*');
if (!$files) return false;
$files = array_diff($files, array(#$_COOKIE['last_file']));
$file = array_rand($files);
setcookie('last_file', $files[$file]);
return $files[$file];
}
$picker = new FilePicker();
$picker->randomFile();
$picker->randomFile(); // never the same as the previous
--
class FilePicker
{
private $lastFile;
public function randomFile($dir = 'destinations')
{
$files = glob($dir . '/*.*');
do {
$file = array_rand($files);
} while ($this->lastFile == $file);
$this->lastFile = $file;
return $files[$file];
}
}
Essentially: store the name of every file used in an array; every time you pull a new name, check whether it already exists in the array.
in_array() will help you with that. array_push() will help fill the "used files" array.
You could make the array a static one to have the list available whenever you call the function (instead of using global variables).
If you want to present a fixed set of files in random order,
then read all file names into an array, shuffle the array
and then just use the array from start to end.
I have the following code that I am using to randomly display PHP widgets from a folder:
<?php
function random_widget($dir = 'wp-content/themes/zonza/elements')
{
$files = glob($dir . '/*.*');
$file = array_rand($files);
return $files[$file];
}
?>
<?php include random_widget();?>
<?php include random_widget();?>
<?php include random_widget();?>
random_widget(); outputs a URL, which I then use in the include function to display the widget.
The code randomly chooses between 6 php files and displays one randomly. I include it 3 times to get 3 widgets. However, I get the same widget displayed more than once sometimes.
What can I do to modify the code to prevent this from happening?
Try this:
<?php
function random_widget($dir = 'wp-content/themes/zonza/elements')
{
static $files = false;
if(!$files) $files=glob($dir . '/*.*');
$key = array_rand($files);
$file=$files[$key];
unset($files[$key]);
return $file;
}
?>
It works by removing the file returned from $files, and maintaining $files over multiple function calls (it only globs() on the first time you call the function)
Declare files at the beginning of the page $files = glob($dir . '/*.*');
In random_widget, do this unset($files[$file]) after you pull the value.
array_rand takes a number $num_req as an optional second parameter which specifies the number of entries you want to pick. So add this parameter to random_widget, pass it to array_rand to get an array of keys instead of a single key, return the array of files, and then iterate over this array to include the widgets (instead of calling random_widget three times).
<?php
function random_widget($num_req, $dir = 'wp-content/themes/zonza/elements')
{
$files = glob($dir . '/*.*');
$keys = array_rand($files, $num_req);
$chosen = array();
foreach($keys as $key) {
$chosen[] = $files[$key];
}
return $chosen;
}
$widgets = random_widget(3);
foreach($widgets as $widget) {
include $widget;
}
?>
An advantage of this solution over the ones proposed in the other answers is that it is stateless: you can reuse the function in different contexts as much as you want.
Source: http://php.net/manual/en/function.array-rand.php
Does anyone have a brilliant idea how to obtain the elements with the deepest path from an array with file paths? If this sounds weird, imagine the following array:
/a/b
/a
/1/2/3/4
/1/2
/1/2/3/5
/a/b/c/d/e
What I want to obtain is:
/1/2/3/4
/1/2/3/5
/a/b/c/d/e
Wondering what the fastest method is without having to iterate over the whole array over and over again. Language is PHP (5.2).
Following your clarifications, here's a function that would do it. It keeps an array of the "deepest paths" found and compares each path against it. The best-case scenario is O(n) (if all paths are subpaths of the largest one) and worst-case scenario is O(n2) (if all paths are completely distinct).
Note that continue 2 means "continue on the outer loop".
<?php
function getDeepestPaths($array)
{
$deepestPaths = array();
foreach ($array as $path)
{
$pathLength = strlen($path);
// look for all the paths we consider the longest
// (note how we're using references to the array members)
foreach ($deepestPaths as &$deepPath)
{
$deepPathLength = strlen($deepPath);
// if $path is prefixed by $deepPath, this means that $path is
// deeper, so we replace $deepPath with $path
if (substr($path, 0, $deepPathLength) == $deepPath)
{
$deepPath = $path;
continue 2;
}
// otherwise, if $deepPath is prefixed by $path, this means that
// $path is shallower; so we should stop looking
else if (substr($deepPath, 0, $pathLength) == $path)
{
continue 2;
}
}
// $path matches nothing currently in $deepestPaths, so we should
// add it to the array
$deepestPaths[] = $path;
}
return $deepestPaths;
}
$paths = array('/a/b', '/a', '/1/2/3/4', '/1/2', '/1/2/3/5', '/a/b/c/d/e');
print_r(getDeepestPaths($paths));
?>
If your folder names don't end with slashes, you'll want to do an additional check in the two ifs: that the character next to the prefix in the deeper path is a slash, because otherwise a path like /foo/bar will be seen as a "deeper path" than /foo/b (and will replace it).
if (substr($path, 0, $deepPathLength) == $deepPath && $path[$deepPathLength] == '/')
if (substr($deepPath, 0, $path) == $path && $deepPath[$path] == '/')
$aPathes = array(
'/a/b',
'/a',
'/1/2/3/4',
'/1/2',
'/1/2/3/5',
'/a/b/c/d/e'
);
function getDepth($sPath) {
return substr_count($sPath, '/');
}
$aPathDepths = array_map('getDepth', $aPathes);
arsort($aPathDepths);
foreach ($aPathDepths as $iKey => $iDepth) {
echo $aPathes[$iKey] . "\n";
}
Also see this example.
=== UPDATE ===
$aUsed = array();
foreach ($aPathes as $sPath) {
foreach ($aUsed as $iIndex => $sUsed) {
if (substr($sUsed, 0, strlen($sPath)) == $sPath || substr($sPath, 0, strlen($sUsed)) == $sUsed) {
if (strlen($sUsed) < strlen($sPath)) {
array_splice($aUsed, $iIndex, 1);
$aUsed[] = $sPath;
}
continue 2;
}
}
$aUsed[] = $sPath;
}
Also see this example.
If you can guarantee that the "spelling" is always the same (ie: "/a/b c/d" vs. /a/b\ /c/d) then you should be able to do some simple string comparation to see if one of the strings is fully contained within the other. If that is true discard the string.
Note that you will need to compare in both directions.