Accessing PHP associative array with index represented as string - php

Is it possible to do something like this in PHP?
$index1 = "[0][1][2]";
$index2 = "['cat']['cow']['dog']";
// I want this to be $myArray[0][1][2]
$myArray{$index1} = 'stuff';
// I want this to be $myArray['cat']['cow']['dog']
$myArray{$index2} = 'morestuff';
I've searched for a solution, but I don't think I know the keywords involved in figuring this out.

eval('$myArray[0][1][2] = "stuff";');
eval('$myArray'.$index1.' = "stuff";');
But be careful when using eval and user input as it is vulnerable to code injection attacks.

Not directly. $myArray[$index] would evaluate to $myArray['[0][1][2]']. You would probably have to separate each dimension or write a little function to interpret the string:
function strIndexArray($arr, $indices, $offset = 0) {
$lb = strpos($indices, '[', $offset);
if ($lb === -1) {
return $arr[$indices];
}
else {
$rb = strpos($indices,']', $lb);
$index = substr($indices, $lb, $rb - $lb);
return strIndexArray($arr[$index], substr($indices, $rb+1));
}
}
You can probably find some regular expression to more easily extract the indices which would lead to something like:
$indices = /*regex*/;
$value = '';
foreach($indices as $index) {
$value = $array[$index];
}
To set a value in the array the following function could be used:
function setValue(&$arr, $indices, $value) {
$lb = strpos($indices, '[');
if ($lb === -1) {
$arr = $value;
}
else {
$rb = strpos($indices, ']', $lb);
$index = substr($indices, $lb, $rb);
setValue($arr[$index], substr($indices, $lb, $rb+1), $value);
}
}
Note: I made above code in the answer editor so it may contain a typo or two ; )

$index1 = "[0][1][2]";
$index2 = "['cat']['cow']['dog']";
function myArrayFunc(&$myArray,$myIndex,$myData) {
$myIndex = explode('][',trim($myIndex,'[]'));
$m = &$myArray;
foreach($myIndex as $myNode) {
$myNode = trim($myNode,"'");
$m[$myNode] = NULL;
$m = &$m[$myNode];
}
$m = $myData;
}
// I want this to be $myArray[0][1][2]
myArrayFunc($myArray,$index1,'stuff');
// I want this to be $myArray['cat']['cow']['dog']
myArrayFunc($myArray,$index2,'morestuff');
var_dump($myArray);

There's always the evil eval:
eval('$myArray' . $index1 . ' = "stuff";');

You can use two anonymous functions for this.
$getThatValue = function($array){ return $array[0][1][2]; };
$setThatValue = function(&$array, $val){ $array[0][1][2] = $val; };
$setThatValue($myArray, 'whatever');
$myValue = $getThatValue($myArray);

Related

PHP sort array by same value twice

I have array with show names like this:
$shows = array('morning_show_15_02_2014_part2.mp3',
'morning_show_15_02_2014_part1.mp3',
'morning_show_14_02_2014_part2.mp3',
'morning_show_14_02_2014_part1.mp3',
'morning_show_13_02_2014_part2.mp3',
'morning_show_13_02_2014_part1.mp3');
So the list look like:
morning_show_15_02_2014_part2.mp3
morning_show_15_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
This is what i get when i loop the directory.
But the list should look like this:
morning_show_15_02_2014_part1.mp3
morning_show_15_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
Still ordered by date, but part 1 is first and then comes part 2.
How can i get this list into right order?
Thank you for any help!
Resolved!
Code is prett nasty but i got what i was looking for:
public function getMp3ListAsJSONArray() {
$songs = array();
$mp3s = glob($this->files_path . '/*.mp3');
foreach ($mp3s as $key => $mp3Source) {
$mp3Source = basename($mp3Source);
$mp3Title = substr($mp3Source, 4);
$mp3Title = substr($mp3Title, 0, -4);
$mp3Title = basename($mp3Source, ".mp3");
$mp3Title = str_replace('_', ' ', $mp3Title);
$mp3Title = ucfirst($mp3Title);
$songs[$key]['title'] = $mp3Title;
$songs[$key]['mp3'] = urldecode($this->files_url . '/' . $mp3Source);
}
rsort($songs);
$pairCounter = 1;
$counter = 0;
foreach ($songs as $key => $value) {
$playlist[$pairCounter][] = $value;
$counter = $counter + 1;
if($counter == 2) {
$pairCounter = $pairCounter + 1;
$counter = 0;
}
}
foreach ($playlist as $show) {
$finalList[] = $show[1];
$finalList[] = $show[0];
}
$finalList = json_encode($finalList);
return $finalList;
}
Output is like i described in the topic.
Try to use array sort
Here is an example for you
http://techyline.com/php-sorting-array-with-unique-value/
You must definitely write your own string comparision function. Remember that you have 2 different comparisons. The first compares the first parts for the filenames as strings. The second part compares the numbers, where 20 comes after 2. This is a natural number sorting for the second part. The third part is after the last dot in the filename. This will be ignored.
<?php
function value_compare($a, $b) {
$result = 0;
$descending = TRUE;
$positionA = strpos($a, 'part');
$positionB = strpos($b, 'part');
if ($positionA === $positionB) {
$compareFirstPart = substr_compare($a, $b, 0, $positionA + 1);
if ($compareFirstPart === 0) {
$length = 0;
$offset = $positionA + strlen('part');
$positionDotA = strrpos($a, '.');
$positionDotB = strrpos($b, '.');
$part2A = '';
$part2B = '';
if ($positionDotA !== FALSE) {
$part2A = substr($a, $offset, $positionDotA);
} else {
$part2A = substr($a, $offset);
}
if ($positionDotB !== FALSE) {
$part2B = substr($b, $offset, $positionDotB);
} else {
$part2B = substr($b, $offset);
}
$result = strnatcmp($part2A, $part2B);
} else {
$result = $compareFirstPart;
if ($descending) {
$result = -$result;
}
}
}
return $result;
}
$shows = array('morning_show_15_02_2014_part2.mp3', 'morning_show_15_02_2014_part1.mp3', 'morning_show_14_02_2014_part2.mp3', 'morning_show_14_02_2014_part1.mp3', 'morning_show_13_02_2014_part2.mp3', 'morning_show_13_02_2014_part1.mp3');
usort($shows, 'value_compare');
var_dump($shows);
?>

Simplify and Abstract my Code: Combining Strings

I want to combine strings in PHP. My script creates every possible combination like below.
$part1 = array('','d','n','s','g');
$part2 = array('a','e','o','oo');
$part3 = array('m','n','s','d','l','t','g','j','p');
$part4 = array('g','p','l','');
$part5 = array('g','p','l');
$part6 = array('a','e','o');
$part7 = array('d','l','r','');
$names = array();
foreach ($part1 as $letter1) {
foreach ($part2 as $letter2) {
foreach ($part3 as $letter3) {
foreach ($part4 as $letter4) {
foreach ($part5 as $letter5) {
foreach ($part6 as $letter6) {
foreach ($part7 as $letter7) {
$names[] = $letter1 . $letter2 . $letter3 . $letter4 . $letter5 . $letter6 . $letter7;
}
}
}
}
}
}
}
But I am not happy with my solution. I is quick and dirty code. Is there a solution wich works with a flexible number of part arrays, so I can extend the script by e.g. $part8 easiely? (without changing the loop construction)
Recursive one:
function buildNames( $parts, $chars = ''){
// Nothing to do, shouldn't happen
if( !count( $parts)){
return array();
}
$names = array();
$part = array_shift( $parts);
// Max level, we can build final names from characters
if( !count( $parts)){
foreach( $part as $char){
$names[] = $chars . $char;
}
return $names;
}
// "We need to go deeper" and build one more level with remembered chars so far
foreach( $part as $char){
$names = array_merge( $names, buildNames( $parts, $chars . $char));
}
return $names;
}
$parts = array( $part1, $part2, $part3, $part4, $part5, $part6, $part7);
$names = buildNames( $parts);
From head, from scratch, comment if something, but idea should be good
You could reduce this problem to six cartesian products:
cartesianProduct($part1,
cartesianProduct($part2,
cartesianProduct($part3,
cartesianProduct($part4,
cartesianProduct($part5,
cartesianProduct($part6, $part7))))));
function cartesianProduct($p1, $p2) {
$ret = array();
foreach($p1 as $l1)
foreach($p2 as $l2)
$ret[] = $l1 . $l2;
return $ret;
}

Check if key exists in $_SESSION by building index string

I need to check if a key exists and return its value if it does.
Key can be an array with subkeys or endkey with a value.
$_SESSION['mainKey']['testkey'] = 'value';
var_dump(doesKeyExist('testkey'));
function doesKeyExist($where) {
$parts = explode('/',$where);
$str = '';
for($i = 0,$len = count($parts);$i<$len;$i++) {
$str .= '[\''. $parts[$i] .'\']';
}
$keycheck = '$_SESSION[\'mainKey\']' . $str;
if (isset(${$keycheck})) {
return ${$keycheck};
}
// isset($keycheck) = true, as its non-empty. actual content is not checked
// isset(${$keycheck}) = false, but should be true. ${$var} forces a evaluate content
// isset($_SESSION['mainKey']['testkey']) = true
}
Using PHP 5.3.3.
Instead of building the string, just check if the key exists within your loop.
For instance:
function doesKeyExist($where) {
$parts = explode('/',$where);
$currentPart = $_SESSION['mainKey'];
foreach($parts as $part) {
if (!isset($currentPart[$part])) {
return false;
}
$currentPart = $currentPart[$part];
}
return true;
}
function getByKeys($keys, $array) {
$value = $array;
foreach (explode('/', $keys) as $key) {
if (isset($value[$key])) {
$value = $value[$key];
} else {
return null;
}
}
return $value;
}
Perhaps I'm misunderstanding the question, but this would appear to be the simplest way of doing it:
function getKey($arr, $key) {
if (array_key_exists($key, $arr)) {
return $arr[$key];
} else {
return false;
}
}
$value = getKey($_SESSION['mainKey'], 'testkey');
You should use $$keycheck, not ${$keycheck}.
The last notation is only if you use the variable inside a string (e.g. "${$keycheck}")
See http://php.net/manual/en/language.variables.variable.php for more details about variable variables
You might want to use the eval() php function for this.
function doesKeyExist($where) {
$parts = explode('/',$where);
$str = '';
for($i = 0,$len = count($parts);$i<$len;$i++) {
$str .= '["'. $parts[$i] .'"]';
}
eval('$keycheck = $_SESSION["mainKey"]' . $str . ';');
if (isset($keycheck)) {
return $keycheck;
}
}
HTH

PHP recursive variable replacement

I'm writing code to recursively replace predefined variables from inside a given string. The variables are prefixed with the character '%'. Input strings that start with '^' are to be evaluated.
For instance, assuming an array of variables such as:
$vars['a'] = 'This is a string';
$vars['b'] = '123';
$vars['d'] = '%c'; // Note that $vars['c'] has not been defined
$vars['e'] = '^5 + %d';
$vars['f'] = '^11 + %e + %b*2';
$vars['g'] = '^date(\'l\')';
$vars['h'] = 'Today is %g.';
$vars['input_digits'] = '*****';
$vars['code'] = '%input_digits';
The following code would result in:
a) $str = '^1 + %c';
$rc = _expand_variables($str, $vars);
// Result: $rc == 1
b) $str = '^%a != NULL';
$rc = _expand_variables($str, $vars);
// Result: $rc == 1
c) $str = '^3+%f + 3';
$rc = _expand_variables($str, $vars);
// Result: $rc == 262
d) $str = '%h';
$rc = _expand_variables($str, $vars);
// Result: $rc == 'Today is Monday'
e) $str = 'Your code is: %code';
$rc = _expand_variables($str, $vars);
// Result: $rc == 'Your code is: *****'
Any suggestions on how to do that? I've spent many days trying to do this, but only achieved partial success. Unfortunately, my last attempt managed to generate a 'segmentation fault'!!
Help would be much appreciated!
Note that there is no check against circular inclusion, which would simply lead to an infinite loop. (Example: $vars['s'] = '%s'; ..) So make sure your data is free of such constructs.
The commented code
// if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
// && strpos($expanded.'', '.')===false)) {
..
// }
can be used or skipped. If it is skipped, any replacement is quoted, if the string $str will be evaluated later on! But since PHP automatically converts strings to numbers (or should I say it tries to do so??) skipping the code should not lead to any problems.
Note that boolean values are not supported! (Also there is no automatic conversion done by PHP, that converts strings like 'true' or 'false' to the appropriate boolean values!)
<?
$vars['a'] = 'This is a string';
$vars['b'] = '123';
$vars['d'] = '%c';
$vars['e'] = '^5 + %d';
$vars['f'] = '^11 + %e + %b*2';
$vars['g'] = '^date(\'l\')';
$vars['h'] = 'Today is %g.';
$vars['i'] = 'Zip: %j';
$vars['j'] = '01234';
$vars['input_digits'] = '*****';
$vars['code'] = '%input_digits';
function expand($str, $vars) {
$regex = '/\%(\w+)/';
$eval = substr($str, 0, 1) == '^';
$res = preg_replace_callback($regex, function($matches) use ($eval, $vars) {
if(isset($vars[$matches[1]])) {
$expanded = expand($vars[$matches[1]], $vars);
if($eval) {
// Special handling since $str is going to be evaluated ..
// if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
// && strpos($expanded.'', '.')===false)) {
$expanded = "'$expanded'";
// }
}
return $expanded;
} else {
// Variable does not exist in $vars array
if($eval) {
return 'null';
}
return $matches[0];
}
}, $str);
if($eval) {
ob_start();
$expr = substr($res, 1);
if(eval('$res = ' . $expr . ';')===false) {
ob_end_clean();
die('Not a correct PHP-Expression: '.$expr);
}
ob_end_clean();
}
return $res;
}
echo expand('^1 + %c',$vars);
echo '<br/>';
echo expand('^%a != NULL',$vars);
echo '<br/>';
echo expand('^3+%f + 3',$vars);
echo '<br/>';
echo expand('%h',$vars);
echo '<br/>';
echo expand('Your code is: %code',$vars);
echo '<br/>';
echo expand('Some Info: %i',$vars);
?>
The above code assumes PHP 5.3 since it uses a closure.
Output:
1
1
268
Today is Tuesday.
Your code is: *****
Some Info: Zip: 01234
For PHP < 5.3 the following adapted code can be used:
function expand2($str, $vars) {
$regex = '/\%(\w+)/';
$eval = substr($str, 0, 1) == '^';
$res = preg_replace_callback($regex, array(new Helper($vars, $eval),'callback'), $str);
if($eval) {
ob_start();
$expr = substr($res, 1);
if(eval('$res = ' . $expr . ';')===false) {
ob_end_clean();
die('Not a correct PHP-Expression: '.$expr);
}
ob_end_clean();
}
return $res;
}
class Helper {
var $vars;
var $eval;
function Helper($vars,$eval) {
$this->vars = $vars;
$this->eval = $eval;
}
function callback($matches) {
if(isset($this->vars[$matches[1]])) {
$expanded = expand($this->vars[$matches[1]], $this->vars);
if($this->eval) {
// Special handling since $str is going to be evaluated ..
if(!is_numeric($expanded) || (substr($expanded . '', 0, 1)==='0'
&& strpos($expanded . '', '.')===false)) {
$expanded = "'$expanded'";
}
}
return $expanded;
} else {
// Variable does not exist in $vars array
if($this->eval) {
return 'null';
}
return $matches[0];
}
}
}
I now have written an evaluator for your code, which addresses the circular reference problem, too.
Use:
$expression = new Evaluator($vars);
$vars['a'] = 'This is a string';
// ...
$vars['circular'] = '%ralucric';
$vars['ralucric'] = '%circular';
echo $expression->evaluate('%circular');
I use a $this->stack to handle circular references. (No idea what a stack actually is, I simply named it so ^^)
class Evaluator {
private $vars;
private $stack = array();
private $inEval = false;
public function __construct(&$vars) {
$this->vars =& $vars;
}
public function evaluate($str) {
// empty string
if (!isset($str[0])) {
return '';
}
if ($str[0] == '^') {
$this->inEval = true;
ob_start();
eval('$str = ' . preg_replace_callback('#%(\w+)#', array($this, '_replace'), substr($str, 1)) . ';');
if ($error = ob_get_clean()) {
throw new LogicException('Eval code failed: '.$error);
}
$this->inEval = false;
}
else {
$str = preg_replace_callback('#%(\w+)#', array($this, '_replace'), $str);
}
return $str;
}
private function _replace(&$matches) {
if (!isset($this->vars[$matches[1]])) {
return $this->inEval ? 'null' : '';
}
if (isset($this->stack[$matches[1]])) {
throw new LogicException('Circular Reference detected!');
}
$this->stack[$matches[1]] = true;
$return = $this->evaluate($this->vars[$matches[1]]);
unset($this->stack[$matches[1]]);
return $this->inEval == false ? $return : '\'' . $return . '\'';
}
}
Edit 1: I tested the maximum recursion depth for this script using this:
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEF'; // GHIJKLMNOPQRSTUVWXYZ
$length = strlen($alphabet);
$vars['a'] = 'Hallo World!';
for ($i = 1; $i < $length; ++$i) {
$vars[$alphabet[$i]] = '%' . $alphabet[$i-1];
}
var_dump($vars);
$expression = new Evaluator($vars);
echo $expression->evaluate('%' . $alphabet[$length - 1]);
If another character is added to $alphabet maximum recursion depth of 100 is reached. (But probably you can modify this setting somewhere?)
I actually just did this while implementing a MVC framework.
What I did was create a "find-tags" function that uses a regular expression to find all things that should be replaced using preg_match_all and then iterated through the list and called the function recursively with the str_replaced code.
VERY Simplified Code
function findTags($body)
{
$tagPattern = '/{%(?P<tag>\w+) *(?P<inputs>.*?)%}/'
preg_match_all($tagPattern,$body,$results,PREG_SET_ORDER);
foreach($results as $command)
{
$toReturn[] = array(0=>$command[0],'tag'=>$command['tag'],'inputs'=>$command['inputs']);
}
if(!isset($toReturn))
$toReturn = array();
return $toReturn;
}
function renderToView($body)
{
$arr = findTags($body);
if(count($arr) == 0)
return $body;
else
{
foreach($arr as $tag)
{
$body = str_replace($tag[0],$LOOKUPARRY[$tag['tag']],$body);
}
}
return renderToView($body);
}

PHP Remove JavaScript

I am trying to remove JavaScript from the HTML.
I can't get the regular expression to work with PHP; it's giving me an null array. Why?
<?php
$var = '
<script type="text/javascript">
function selectCode(a)
{
var e = a.parentNode.parentNode.getElementsByTagName(PRE)[0];
if (window.getSelection)
{
var s = window.getSelection();
if (s.setBaseAndExtent)
{
s.setBaseAndExtent(e, 0, e, e.innerText.length - 1);
}
else
{
var r = document.createRange();
r.selectNodeContents(e);
s.removeAllRanges();
s.addRange(r);
}
}
else if (document.getSelection)
{
var s = document.getSelection();
var r = document.createRange();
r.selectNodeContents(e);
s.removeAllRanges();
s.addRange(r);
}
else if (document.selection)
{
var r = document.body.createTextRange();
r.moveToElementText(e);
r.select();
}
}
</script>
';
function remove_javascript($java){
echo preg_replace('/<script\b[^>]*>(.*?)<\/script>/i', "", $java);
}
?>
this should do it:
echo preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', "", $var);
/s is so that the dot . matches newlines too.
Just a warning, you should not use this type of regexp to sanitize user input for a website. There is just too many ways to get around it. For sanitizing use something like the http://htmlpurifier.org/ library
This might do more than you want, but depending on your situation you might want to look at strip_tags.
Here's an idea
while (true) {
if ($beginning = strpos($var,"<script")) {
$stringLength = (strpos($var,"</script>") + strlen("</script>")) - $beginning;
substr_replace($var, "", $beginning, $stringLength);
} else {
break
}
}
In your case you could regard the string as a list of newline delimited strings and remove the lines containing the script tags(first & second to last) and you wouldn't even need regular expressions.
Though if what you are trying to do is preventing XSS it might not be sufficient to only remove script tags.
function clean_jscode($script_str) {
$script_str = htmlspecialchars_decode($script_str);
$search_arr = array('<script', '</script>');
$script_str = str_ireplace($search_arr, $search_arr, $script_str);
$split_arr = explode('<script', $script_str);
$remove_jscode_arr = array();
foreach($split_arr as $key => $val) {
$newarr = explode('</script>', $split_arr[$key]);
$remove_jscode_arr[] = ($key == 0) ? $newarr[0] : $newarr[1];
}
return implode('', $remove_jscode_arr);
}
You can remove any JavaScript code from HTML string with the help of following PHP function
You can read more about it here:
https://mradeveloper.com/blog/remove-javascript-from-html-with-php
function sanitizeInput($inputP)
{
$spaceDelimiter = "#BLANKSPACE#";
$newLineDelimiter = "#NEWLNE#";
$inputArray = [];
$minifiedSanitized = '';
$unMinifiedSanitized = '';
$sanitizedInput = [];
$returnData = [];
$returnType = "string";
if($inputP === null) return null;
if($inputP === false) return false;
if(is_array($inputP) && sizeof($inputP) <= 0) return [];
if(is_array($inputP))
{
$inputArray = $inputP;
$returnType = "array";
}
else
{
$inputArray[] = $inputP;
$returnType = "string";
}
foreach($inputArray as $input)
{
$minified = str_replace(" ",$spaceDelimiter,$input);
$minified = str_replace("\n",$newLineDelimiter,$minified);
//removing <script> tags
$minifiedSanitized = preg_replace("/[<][^<]*script.*[>].*[<].*[\/].*script*[>]/i","",$minified);
$unMinifiedSanitized = str_replace($spaceDelimiter," ",$minifiedSanitized);
$unMinifiedSanitized = str_replace($newLineDelimiter,"\n",$unMinifiedSanitized);
//removing inline js events
$unMinifiedSanitized = preg_replace("/([ ]on[a-zA-Z0-9_-]{1,}=\".*\")|([ ]on[a-zA-Z0-9_-]{1,}='.*')|([ ]on[a-zA-Z0-9_-]{1,}=.*[.].*)/","",$unMinifiedSanitized);
//removing inline js
$unMinifiedSanitized = preg_replace("/([ ]href.*=\".*javascript:.*\")|([ ]href.*='.*javascript:.*')|([ ]href.*=.*javascript:.*)/i","",$unMinifiedSanitized);
$sanitizedInput[] = $unMinifiedSanitized;
}
if($returnType == "string" && sizeof($sanitizedInput) > 0)
{
$returnData = $sanitizedInput[0];
}
else
{
$returnData = $sanitizedInput;
}
return $returnData;
}
this was very usefull for me. try this code.
while(($pos = stripos($content,"<script"))!==false){
$end_pos = stripos($content,"</script>");
$start = substr($content, 0, $pos);
$end = substr($content, $end_pos+strlen("</script>"));
$content = $start.$end;
}
$text = strip_tags($content);
I use this:
function clear_text($s) {
$do = true;
while ($do) {
$start = stripos($s,'<script');
$stop = stripos($s,'</script>');
if ((is_numeric($start))&&(is_numeric($stop))) {
$s = substr($s,0,$start).substr($s,($stop+strlen('</script>')));
} else {
$do = false;
}
}
return trim($s);
}

Categories