Scenario/Problem Isolation: Lets suppose my program uses MULTIPLE variables. At the program beginning I want to manipulate MANY of the variables AT ONCE through a general function with LITTLE CODE, before then later in the process using only distinctive few variables in specific functions.
Question: How do I pass multiple variables by reference to a foreach loop? Or is there a better/alternative method for looping through multiple determined variables?
Post(s) related to topic, but didn't solve my issue:
PHP foreach loop on multiple objects?
Background (for those concerned): I have a command line program which uses getopts http://hash-bang.net/2008/12/missing-php-functions-getopts/ to get various arguments, thus I get about 20 variables. I want to run all variables, which contain filepath(s) (about 10) through the "general" function reduceHierarchyDots() at ONCE (instead of calling the function 10 times).
<?php
/// The "general" function:
function reduceHierarchyDots ($file) {
while (preg_match('|./\.{2}/|', $file)) { $file = preg_replace('|/([^/]+)/\.{2}/|', '/', $file, 1); }
$file = preg_replace('|(/(\./)+)|', '/', $file);
$file = preg_replace('|^(\./)+|', '', $file);
return $file;
}
function reduceHierarchyDotsRef (&$file) {
while (preg_match('|./\.{2}/|', $file)) { $file = preg_replace('|/([^/]+)/\.{2}/|', '/', $file, 1); }
$file = preg_replace('|(/(\./)+)|', '/', $file);
$file = preg_replace('|^(\./)+|', '', $file);
}
/// The "many" variables:
$x = "something";
$y = 123;
$y = array ("a", "B", 3);
$a = "/Users/jondoe/Desktop/source/0.txt";
$b = "/Users/jondoe/Desktop/source/../1.txt";
$c = "/Users/jondoe/Desktop/source/../../2.txt";
$arrOne = array (
"v1" => "/some/thing/../1.pdf",
"v2" => "/some/thing/../../2.pdf",
"v3" => "/some/thing/../../../3.pdf"
);
$arrTwo = array (
"./1.doc",
"/so.me/.thing/ends./././2.doc",
"./././3.doc"
);
/// At the beginning I want to run multiple determined variables through a "general" function:
/// Debugging: Variables BEFORE the manipulation:
echo("BEFORE:\n"); var_dump($b, $arrOne["v2"], $arrTwo[2]); echo("\n");
/// Method works, but is long! (1 line/statement per function call)
reduceHierarchyDotsRef($b);
reduceHierarchyDotsRef($arrOne["v2"]);
reduceHierarchyDotsRef($arrTwo[2]);
/// Hence, I'd like to pass all variables by reference at once to a foreach loop:
//// These cause: Parse error: syntax error, unexpected '&':
// foreach ( array($b, $arrOne["v2"], $arrTwo[2] ) as &$file) { $file = reduceHierarchyDots($file); }
// foreach (array(&$b, &$arrOne["v2"], &$arrTwo[2] ) as &$file) { $file = reduceHierarchyDotsRef($file); }
//// These have no effect on the intended variables:
// foreach (array(&$b, &$arrOne["v2"], &$arrTwo[2] ) as $file) { $file = reduceHierarchyDots($file); }
// foreach (array(&$b, &$arrOne["v2"], &$arrTwo[2] ) as $file) { $file = reduceHierarchyDotsRef($file); }
/// Debugging: Variables AFTER the manipulation:
echo("AFTER:\n"); var_dump($b, $arrOne["v2"], $arrTwo[2]);
/// After the "general" function ran over various variables, the more specific actions happen: ...
?>
You could try generating an array of the variable names, then using variable variables:
$x = '/bees/../ham';
$y = 'some/other/path';
$arr = array('x', 'y');
foreach($arr as $item) {
reduceHierarchyDotsRef($$item);
}
not sure if this works with passing by reference, but I see not reason for it not to work.
Pass by reference is defined in the function signature:
function func(&$passByRef);
That's why your code is throwing errors.
See: http://php.net/manual/en/language.references.pass.php
Related
I saved template-variables in the DB, e.g.: slider.item1.headline1 = "Headline1".
I am using symfony framework. If I just pass "slider.item1.headline1" to the Twig template, that won't be used because the points are interpreted as a multidimensional array (or nested object) (https://symfony.com/doc/current/templates.html#template-variables).
I've now built a routine that converts the string into a multidimensional array.
But I don't like the eval(), PHP-Storm doesn't like it either, marks it as an error.
How could this be solved in a nicer way (without eval)?
Here my method:
protected function convertTemplateVarsFromDatabase($tplvars): array
{
$myvar = [];
foreach ($tplvars as $tv)
{
$handle = preg_replace('/[^a-zA-Z0-9._]/', '_', $tv['handle']);
$tplsplit = explode('.', $handle);
$mem = "";
foreach ($tplsplit as $eitem)
{
$mem .= "['" . $eitem . "']";
}
$content = $tv['htmltext'];
eval('$myvar' . $mem . ' = $content;');
}
return $myvar;
}
You can indeed avoid eval here. Maintain a variable that follows the existing array structure of $myvar according to the path given, and let it create any missing key while doing so. This is made easier using the & syntax, so to have a reference to a particular place in the nested array:
function convertTemplateVarsFromDatabase($tplvars): array
{
$myvar = [];
foreach ($tplvars as $tv)
{
$handle = preg_replace('/[^a-zA-Z0-9._]/', '_', $tv['handle']);
$tplsplit = explode('.', $handle);
$current = &$myvar;
foreach ($tplsplit as $eitem)
{
if (!isset($current[$eitem])) $current[$eitem] = [];
$current = &$current[$eitem];
}
$current = $tv['htmltext'];
}
return $myvar;
}
I'm trying to get the file names from the folder inside a folder ( there is a folder named 1 inside of ../merchant_assets/ folder and I'm trying to get all of the name files inside of that folder )
The code below works fine, however when I assigned my array that consist of the result $temp to the stdClass() variable $container it becomes empty when i try to print out the array outside of the function print_r($container->screenshots) but it works if i print it out inside of the function
<?php
include 'config.php';
$url = "http://" . $_SERVER['HTTP_HOST'] . '/merchant_assets/';
$target = '../merchant_assets';
$merchant_id = 1;
$container = new stdClass();
$folder = '';
// Call the function
dir_contents_recursive($target);
// Get all screenshots images from merchant_assets folder based on merchant_id
function dir_contents_recursive($dir) {
// open handler for the directory
global $container;
$iter = new DirectoryIterator($dir);
$temp = Array();
foreach( $iter as $item ) {
// make sure you don't try to access the current dir or the parent
if ($item != '.' && $item != '..') {
if( $item->isDir() ) {
// call the function on the folder
global $merchant_id, $folder;
if($merchant_id == $item->getFilename()) {
$folder = $item->getFilename();
dir_contents_recursive("$dir/$item");
}else
continue;
} else {
// print files
global $url, $folder;
$current_index = count($temp);
$temp[$current_index] = $url . $folder . '/' . $item->getFilename();
}
}
}
$container->screenshots = $temp;
print_r($container->screenshots); // It shows the results
}
// Handle response
$response = $container;
print_r($container->screenshots); // No results???
$response_json = json_encode($response);
echo $response_json;
?>
I expected the output to be
[ I intentionally changed the value of each index to item ]
Array (
[0] => item
[1] => item
[2] => item
[3] => item
The problem is your recursive call not assign the data to $temp.
In the begin of your function you decleare $temp = Array(); - but you never use global on him.
So the first time the function called, $temp is empty array and if( $item->isDir() ) is TRUE so it gets to the recursive. In there you do add element to $temp and assign $container->screenshots (that way the prints goes well).
However, after you exit the recursive call you again assign $temp to $container->screenshots but in this scope $temp is empty array! (there for no result).
I strongly recomend not using global here but to return array of file as the recursive function return argument.
I am creating a script that will locate a field in a text file and get the value that I need.
First used the file() function to load my txt into an array by line.
Then I use explode() to create an array for the strings on a selected line.
I assign labels to the array's to describe a $Key and a $Value.
$line = file($myFile);
$arg = 3
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
This works fine but that is a lot of code to have to do over and over again for everything I want to get out of the txt file. So I wanted to create a function that I could call with an argument that would return the value of $key and $val. And this is where I am failing:
<?php
/**
* #author Jason Moore
* #copyright 2014
*/
global $line;
$key = '';
$val = '';
$myFile = "player.txt";
$line = file($myFile); //file in to an array
$arg = 3;
$Character_Name = 3
function get_plr_data2($arg){
global $key;
global $val;
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
return;
}
get_plr_data2($Character_Name);
echo "This character's ",$key,' is ',$val;
?>
I thought that I covered the scope with setting the values in the main and then setting them a global within the function. I feel like I am close but I am just missing something.
I feel like there should be something like return $key,$val; but that doesn't work. I could return an Array but then I would end up typing just as much code to the the info out of the array.
I am missing something with the function and the function argument to. I would like to pass and argument example : get_plr_data2($Character_Name); the argument identifies the line that we are getting the data from.
Any help with this would be more than appreciated.
::Updated::
Thanks to the answers I got past passing the Array.
But my problem is depending on the arguments I put in get_plr_data2($arg) the number of values differ.
I figured that I could just set the Max of num values I could get but this doesn't work at all of course because I end up with undefined offsets instead.
$a = $cdata[0];$b = $cdata[1];$c = $cdata[2];
$d = $cdata[3];$e = $cdata[4];$f = $cdata[5];
$g = $cdata[6];$h = $cdata[7];$i = $cdata[8];
$j = $cdata[9];$k = $cdata[10];$l = $cdata[11];
return array($a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l);
Now I am thinking that I can use the count function myCount = count($c); to either amend or add more values creating the offsets I need. Or a better option is if there was a way I could generate the return array(), so that it would could the number of values given for array and return all the values needed. I think that maybe I am just making this a whole lot more difficult than it is.
Thanks again for all the help and suggestions
function get_plr_data2($arg){
$myFile = "player.txt";
$line = file($myFile); //file in to an array
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
return array($key,$val);
}
Using:
list($key,$val) = get_plr_data2(SOME_ARG);
you can do this in 2 way
you can return both values in an array
function get_plr_data2($arg){
/* do what you have to do */
$output=array();
$output['key'] =$key;
$output['value']= $value;
return $output;
}
and use the array in your main function
you can use reference so that you can return multiple values
function get_plr_data2($arg,&$key,&$val){
/* do job */
}
//use the function as
$key='';
$val='';
get_plr_data2($arg,$key,$val);
what ever you do to $key in function it will affect the main functions $key
I was over thinking it. Thanks for all they help guys. this is what I finally came up with thanks to your guidance:
<?php
$ch_file = "Thor";
$ch_name = 3;
$ch_lvl = 4;
$ch_clss = 15;
list($a,$b)= get_char($ch_file,$ch_name);//
Echo $a,': ',$b; // Out Puts values from the $cdata array.
function get_char($file,$data){
$myFile = $file.".txt";
$line = file($myFile);
$cdata = preg_split('/\s+/', trim($line[$data]));
return $cdata;
}
Brand new to this community, thanks for all the patience.
I have this code and no code before it that refers to any variables seen below. Yet I still can't find why I'm getting the error: "First argument should be an array in..."
$array = array("element here for reason");
function sortdata()
{
$File = fopen("Names.txt", "r");
//put each file line into an array element
while(!feof($File))
{
array_push($array, fgets($File));
}
}
$array is out of scope to the function. You can bring it into scope using global.
$array = ..;
function sortdata() {
global $array;
...
}
sortdata();
Alternatively, you can pass it by reference into the function.
$array = ..;
function sortdata(&$array) {
...
}
sortdata($array);
You use variable $array inside function body. In this case this is local variable and it automatically sets to string.
For work with your global variable $array you should use instruction global in your function.
function sortdata() {
global $array;
/* there your code for work with $array */
}
The issue with the code is that you are not passing the $array variable into the function. Aside from that, it would be more efficient to use the shortcut way to add an item to the array instead of calling array_push since it eliminates the overhead of calling a function.
$array = array("element here for reason");
function sortdata($array)
{
$File = fopen("Names.txt", "r");
//put each file line into an array element
while(!feof($File))
{
$array[] = fgets($File);
}
return $array;
}
You should try to first initialize the array and bring the array within the scope of the function like so:
$array = array();
array_push($array, "element here for reason");
function sortdata()
{
global $array;
$File = fopen("Names.txt", "r");
//put each file line into an array element
while(!feof($File))
{
array_push($array, fgets($File));
}
}
This give you backward compatibility
function sortdata(array $array = array())
{
$File = fopen("Names.txt", "r");
while(!feof($File))
{
array_push($array, fgets($File));
}
return $array;
}
I have some php files that includes some language constants
define("_SEARCH","Search");
define("_LOGIN","Login");
define("_WRITES","writes");
define("_POSTEDON","Posted on");
define("_NICKNAME","Nickname");
now I need to read each file and list all constants and their values
and to return an output like this :
constant name :
value is :
so I think there should be function to list all defined constants of a given php file.
I'm aware of functions like token_get_all or get_defined_constants but i wasn't able to do it.
If the files do contain nothing but define statements, you can use get_defined_constants:
function getUserDefinedConstants() {
$constants = get_defined_constants(true);
return (isset($constants['user']) ? $constants['user'] : array());
}
$constantsBeforeInclude = getUserDefinedConstants();
include('file.php');
$constantsAfterInclude = getUserDefinedConstants();
$newConstants = array_diff_assoc($constantsAfterInclude, $constantsBeforeInclude);
What it does is basically: get_defined_constants(true) gives us an array of arrays with all available constants, sorted by sections (core, user, ..) - the array under the key 'user' gives us all user-defined constants that we defined in our php code using define, up to that point. array_diff_assoc gives us the difference between this array before and after the file got included.. and that is exactly a list of all constants that got defined in that specific file (as long as there is none of the declarations a duplicate, meaning a constant with that exact name has been defined before - but this would cause an error anyway).
this is the php script you need:
<?php
//remove comments
$Text = php_strip_whitespace("your_constants_file.php");
$Text = str_replace("<?php","",$Text);
$Text = str_replace("<?","",$Text);
$Text = str_replace("?>","",$Text);
$Lines = explode(";",$Text);
$Constants = array();
//extract constants from php code
foreach ($Lines as $Line) {
//skip blank lines
if (strlen(trim($Line))==0) continue;
$Line = trim($Line);
//skip non-definition lines
if (strpos($Line,"define(")!==0) continue;
$Line = str_replace("define(\"","",$Line);
//get definition name & value
$Pos = strpos($Line,"\",\"");
$Left = substr($Line,0,$Pos);
$Right = substr($Line,$Pos+3);
$Right = str_replace("\")","",$Right);
$Constants[$Left] = $Right;
}
echo "<pre>";
var_dump($Constants);
echo "</pre>";
?>
The result will be something similar to this:
array(5) {
["_SEARCH"]=>
string(6) "Search"
["_LOGIN"]=>
string(5) "Login"
["_WRITES"]=>
string(6) "writes"
["_POSTEDON"]=>
string(9) "Posted on"
["_NICKNAME"]=>
string(8) "Nickname"
}
Late to the game here but I had a similar issue. You could use an include() substitute/wrapper function that logs constants in an accessible global array.
<?php
function include_build_page_constants($file) {
global $page_constants ;
$before = get_defined_constants(true);
include_once($file);
$after = get_defined_constants(true);
if ( isset($after['user']) ) {
if ( isset($before['user']) ) {
$current = array_diff_assoc($after['user'],$before['user']);
}else{
$current = $after['user'];
}
$page_constants[basename($file)]=$current;
}
}
include_and_build_page_constants('page1.php');
include_and_build_page_constants('page2.php');
// test the array
echo '<pre>'.print_r($page_constants,true).'</pre>';
?>
This will result in something like:
Array
(
[page1.php] => Array
(
[_SEARCH] => Search
[_LOGIN] => Login
[_WRITES] => writes
[_POSTEDON] => Posted on
[_NICKNAME] => Nickname
)
[page2.php] => Array
(
[_THIS] => Foo
[_THAT] => Bar
)
)
Assuming that you want to do this on runtime, you should take a look at PHP Reflection, specifically at the ReflectionClass::getConstants() which lets you do exactly what you seem to want.
I too had the same problem. I went from jondinham's suggestion, but I prefer to use regex, as it is a bit easier to control and flexible. Here's my version of the solution:
$text = php_strip_whitespace($fileWithConstants);
$text = str_replace(array('<?php', '<?', '?>'), '', $text);
$lines = explode(";", $text);
$constants = array();
//extract constants from php code
foreach ($lines as $line) {
//skip blank lines
if (strlen(trim($line)) == 0)
continue;
preg_match('/^define\((\'.*\'|".*"),( )?(.*)\)$/', trim($line), $matches, PREG_OFFSET_CAPTURE);
if ($matches) {
$constantName = substr($matches[1][0], 1, strlen($matches[1][0]) - 2);
$constantValue = $matches[3][0];
$constants[$constantName] = $constantValue;
}
}
print_r($constants);