This function works on one field at a time:
foreach ($GLOBALS as $key => $value) {
if ($key === "special1") {
{$value = preg_replace('/src/','data-machin', $value);
}}}
This one - with no condition (so it tries to proccess 360 fields) does not:
foreach ($GLOBALS as $key => $value)
{$value = preg_replace('/src/','data-machin', $value);
}
and generates this Fatal error: Cannot increment/decrement overloaded objects nor string offsets in /home/xxx/public_html/lib/mysqldb.php on line 145.
How could I make it work?
(Big picture: I am trying to implement a script to delay the loading of photos).
Although it is generally a bad idea to use globals and even worse to do such blind variable operations, you could do it that way:
foreach ($GLOBALS as $key => $value) {
if (is_string($value)) {
$GLOBALS[$key] = preg_replace('/src/','data-machin', $value);
}
}
Updated to include is_string() which checks if the value is a string.
preg_replace only makes sense for string values.
Thus, you may use the is_string function to check values when iterating:
foreach ($GLOBALS as $key => $value) {
if (is_string($value)) {
$value = preg_replace('/src/','data-machin', $value);
}
}
Also please note that this piece of code produces no side effects — i.e. the $GLOBALS array is not modified. You may consider using references instead:
foreach ($GLOBALS as $key => &$value) {
...
}
unset($value);
Related
I know that many people believe it is wrong to edit Superglobals in PHP. But I found myself to always trim user input in every controller of my webapp. To solve the problem once and for all I now just trim all values of these Superglobals like that:
# trim ALL inputs
foreach ($_REQUEST as $key => $value) {
$_REQUEST[$key] = trim($value);
}
foreach ($_COOKIE as $key => $value) {
$_COOKIE[$key] = trim($value);
}
foreach ($_GET as $key => $value) {
$_GET[$key] = trim($value);
}
foreach ($_POST as $key => $value) {
$_POST[$key] = trim($value);
}
Some users might want to define a password that starts or ends with a whitespace. Apart from that I cannot think of any usecase where trimming might do any harm.
Has anybody else tried to trim these Superglobals and ran into strange side effects that I should be aware of?
I'm working with a JSON string. I'm converting it to an associative array to find specific values and change those values when a certain key is found (['content']). The depth of the array is always unknown and will always vary.
Here is the function I wrote. It takes an array as an argument and passes it by reference so that the variable itself is modified rather than a copy of it scoped locally to that function.
$json_array = json_decode($json_string, true);
function replace_data(&$json_array, $data='REPLACE TEST')
{
foreach($json_array as $key => $value) {
if ($key == 'content' && !is_array($value)) {
$json_array[$key] = $data;
} else {
if (is_array($value)) {
replace_data($value, $data);
}
}
}
}
replace_data($json_array, "test test test");
var_dump($json_array);
What I'm expecting to happen is every time a key ['content'] is found at no matter what depth, it replaces with that value specified in the $data argument.
But, when I var_dump($json_array) Those values are unchanged.
What am I missing?
With array_walk_recursive:
function replace_data($json_array, $data = 'REPLACE TEST') {
array_walk_recursive($json_array, function (&$value, $key) use ($data) {
if (!is_array($value) && $key === 'content') {
// $value passed by reference
$value = $data;
}
});
return $json_array;
}
And without references:
function replace_data($json_array, $data = 'REPLACE TEST') {
foreach ($json_array as $key => $value) {
if (is_array($value)) {
$json_array[$key] = replace_data($value, $data);
} elseif ($key === 'content') {
$json_array[$key] = $data;
}
}
return $json_array;
}
To expand on my comment, you need another reference here:
foreach($json_array as $key => &$value) {
That way, a reference to the original value is passed when you make the recursive call, rather than the local copy created with the foreach loop.
From the PHP manual entry for foreach:
In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.
You'll also see in the manual that it recommends unsetting the reference to $value after the loop. Even though it probably won't cause any problems if you don't do that in your function, it's best to be in the habit of always unsetting references created in foreach loops like that. It can cause some strange looking problems if you don't.
From PHP7.4, "arrow function" syntax offers a clean and short approach.
As leafnodes are iterated, if the key is content, then replace the text, otherwise do not change the value.
There are no returns being used. All mutations are applied directly to the passed in variables prefixed with &.
function replace_data(&$array, $replaceWith = 'REPLACE TEST')
{
array_walk_recursive(
$array,
fn(&$v, $k) => $v = ($k === 'content' ? $replaceWith : $v)
);
}
replace_data($json_array, "test test test");
var_export($json_array);
I created a function to iterate through the $_GET array and, after verifying the data, create a constant for each element. However, it only returns the first constant.
function sanitize($key, $value){
$safe_types = array(
"month",
"year",
"course",
"pagetype",
"studentid"
);
if (in_array($key, $safe_types)) {
$key = strtoupper($key);
if (is_numeric($value) || preg_match("#^[a-z_]+#", $value)){
define ($key, $value);
}
}
}
foreach ($_GET as $key => $value) {
sanitize($key, $value);
}
I also tried array_walk with no better result.
Thank you all for taking a look and telling me it worked for you.
The issue was not the $_GET array provided but how I was testing the results. I had the following:
echo
"<br/>month: ".MONTH;
"<br/>course: ".COURSE;
"<br/>pagetype: ".PAGETYPE;
"<br/>studentid: ".STUDENTID;
. . .but of course should have had periods instead of semicolons.
I'm having trouble understading this function. I know what register_globals is and how long it has been depreciated from PHP but I'm looking at this code and I'm like, what in the?...
<?php
//Undo register_globals
function unregister_globals() {
if (ini_get(register_globals)) {
$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
foreach ($array as $value) {
foreach ($GLOBALS[$value] as $key => $var) {
if ($var === $GLOBALS[$key]) {
unset($GLOBALS[$key]);
}
}
}
}
}
?>
The part in which I'm not understanding is this...
foreach ($array as $value) {
foreach ($GLOBALS[$value] as $key => $var) {
if ($var === $GLOBALS[$key]) {
unset($GLOBALS[$key]);
}
}
}
This foreach loop is cycling through each value in $array we defined then the inner foreach loop is grabbing a super global array from GLOBALS whether it is _REQUEST, _SESSION, _SERVER, _ENV, _FILES, etc... Afterwords it seems like the condional is checking to see if $var is equal to a GLOBAL variable or what not. If so then we'll unset it.
But I'm still having a major difficulty wrapping my head around this one...
UPDATE
Here is the snippet of code I'm experimenting with and debugging. What happens if register_globals is on, and a hacker comes barreling along, inserts ?auth=1 into the query string? Will auth be deleted from GLOBALS or will it be echoed out?
if( true ) {
$globals = array(
'_COOKIE',
'_GET',
'_POST',
'_REQUEST',
'_SERVE',
'_SESSION'
);
foreach($globals as $global) {
foreach($GLOBALS[$global] as $k => $v) {
/* $GLOBALS['_GET'] on the first loop; */
/* IF WE SAY, $GLOBALS['app_dir'], WE GET THE VALUE */
if( $v == $GLOBALS[$k] ) {
echo "K=> " . $k . "<br />";
echo "V => " . $v . "<br />";
echo "GLOB => " . $GLOBALS[$k] . "<br />";
}
}
}
echo $authorized; // a intentional non-defined variable
//print_r($GLOBALS);
}
Thanks for your time.
Yes, this code looks weird:
foreach ($GLOBALS[$value] as $key => $var) {
if ($var === $GLOBALS[$key]) {//<- ?
unset($GLOBALS[$key]);
}
}
You can imitate what's going in, using a simple array, and make sure, that this if is absolutely useless:
<?php
$data = array(1,2,3);
foreach ($data as $key => $var) {
var_dump($var);
var_dump($data[$key]);
if ($var === $data[$key]) {
unset($data[$key]);
}
}
output:
int(1)
int(1)
int(2)
int(2)
int(3)
int(3)
As you can see, the values are equal every time, and in the end $data will be empty.
Update
Here is the script, you can reproduce on your machine:
<?php
extract($_REQUEST);
var_dump($auth);
$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
foreach ($array as $value) {
if(isset($GLOBALS[$value]))
{
foreach ($GLOBALS[$value] as $key => $var) {
unset($GLOBALS[$key]);
}
}
}
var_dump($auth);
While calling it like this: http://site/script.php?auth=1
It gives the following output for me:
string(1) "1"
<br />
<b>Notice</b>: Undefined variable: auth on line 14
NULL
So, it seems, that auth variable was destroyed. But $GLOBALS array still contains a lot of data.
Update 2
I think, our error was here:
unset($GLOBALS[$key]);
Why are we unsetting the key of a foreign array. If you do it like this:
$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
foreach ($array as $value) {
if(isset($GLOBALS[$value]))
{
foreach ($GLOBALS[$value] as $key => $var) {
unset($GLOBALS[$value][$key]);
}
}
}
It will empty $_REQUEST, $_SESSION and all the other necessary arrays.
Globals ( especially $_GET, $_POST, $_COOKIE, $_ENV, ... and the $GLOBALS-Entries of this Superglobals ) are initially filled by PHP and should never be (over)written oder deleted by any application.
Any manipulation of Superglobal-Values drops the authenticity of the current request and works against the intentional behavior of PHP.
The nested foreach means that a given Array would be iterated ( outter foreach ) and each entry would be iterated too. The Array-Key is served as $key and the array-value as $var
Now that I was able to see how this actually works, I was able to wrap my head around it. Essentially if users are using PHP 5.3.0 or below and 'register_globals' is enabled, hackers can query common variables names such as 'authorized', 'username', etc to feed in there own values if the variable was already defined by the developer.
Example:
if( is_authorized() ) {
$auth = 1;
}
if( $auth ) {
// do authorization code here!!!
} else {
show_login();
}
You can see $auth is condiotionally defined. If a hacker comes along and tries something like, index.php?auth=1 or index.php?auth=true then PHP will register that as a global value, it'll be checked in the conditional, and our hacker will have access to the application.
I found that...
foreach ($array as $value) {
foreach ($GLOBALS[$value] as $key => $var) {
if ($var === $GLOBALS[$key]) {
unset($GLOBALS[$key]);
}
}
}
... actually unsets these values and the hacker won't have access, however, there is also a bug as I previously thought. I have two globals in my framework called $app_dir and $sys_dir pointing to two important directories. If hackers come along and say something like index.php?app_dir=application&sys_dir=system then it'll actually unset those global variables from $GLOBALS. This calls for a potential danger inside of my framework for anyone using it on PHP 5.3.0 or below.
Basically how this works is the conditional checks to see if $var (our value, 'system') is equal to the same value as what's inside $GLOBAL['sys_dir']. Anyone know how to fix this?
Been experiencing this error for a little while and can't find any conclusive answers on fixing it. I have tried removing quotes from $key in line 59 but to no avail.
if (!get_magic_quotes_gpc()) {
if (isset($_POST)) {
foreach ($_POST as $key => $value) {
$_POST['$key'] = trim(addslashes($value));
}
}
if (isset($_GET)) {
foreach ($_GET as $key => $value) {
$_GET[$key] = trim(addslashes($value));
}
}
}
LINE 59
$_POST['$key'] = trim(addslashes($value));
Error On Screen
Notice: Array to string conversion in
C:\Inetpub\vhosts\domain.com\httpdocs\library\config.php on
line 59
Check if it is array before you assign it
$_POST[$key] = !is_array($value) ? trim(addslashes($value)) : '';
// ^ Remove the quotes here // ^ Do something
// Instead of
// Using empty
According to PHP.net the function addslashes() takes a String type as parameter. Check what type $value is. If it is an array itself then addslashes() may be throwing the error.
PS:
You should use $_POST[$key] rather than $_POST['$key'] if you want to use the value of $key as the index of the $_POST array.
I think you should use this code $_POST[$key] = $value; instead of using this $_POST['$key'] = trim(addslashes($value));
or make a check if the value is in array or not
Do this:
foreach ($_POST as &$value) {
$value = is_array($value) ?
array_map(function($x) { return trim(addslashes($x)); } :
trim(addslashes($value));
}
However, this could still fail if any of your parameters are multi-dimensional arrays. As mentioned in the comments, the right solution is to use prepared queries with parameters, rather than interpolating strings into SQL.