will apache_request_headers function work on NGINX Web
Server. If not what is function for NGINX parallel to it
Some people have written their own functions on the PHP docs site.
https://www.php.net/manual/en/function.apache-request-headers.php
<?php
if( !function_exists('apache_request_headers') ) {
function apache_request_headers() {
$arh = array();
$rx_http = '/\AHTTP_/';
foreach($_SERVER as $key => $val) {
if( preg_match($rx_http, $key) ) {
$arh_key = preg_replace($rx_http, '', $key);
$rx_matches = array();
// do some nasty string manipulations to restore the original letter case
// this should work in most cases
$rx_matches = explode('_', $arh_key);
if( count($rx_matches) > 0 and strlen($arh_key) > 2 ) {
foreach($rx_matches as $ak_key => $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
$arh_key = implode('-', $rx_matches);
}
$arh[$arh_key] = $val;
}
}
return( $arh );
}
}
See if that helps (haven't tried it).
As per the documentation:
https://www.php.net/manual/en/function.apache-request-headers.php
Fetches all HTTP request headers from the current request. Works in the Apache, FastCGI, CLI, FPM and NSAPI server module in Netscape/iPlanet/SunONE webservers.
If you're running PHP via an FPM socket on UNIX, or using the FastCGI interface, this function will work. The fact is begins with apache is misleading, but is hardly atypical of PHP function naming.
Related
With PHP 7.2, each is deprecated. The documentation says:
Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
How can I update my code to avoid using it? Here are some examples:
$ar = $o->me;
reset($ar);
list($typ, $val) = each($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = each($out);
for(reset($broken);$kv = each($broken);) {...}
list(, $this->result) = each($this->cache_data);
// iterating to the end of an array or a limit > the length of the array
$i = 0;
reset($array);
while( (list($id, $item) = each($array)) || $i < 30 ) {
// code
$i++;
}
When I execute the code on PHP 7.2 I receive the following error:
Deprecated: The each() function is deprecated. This message will be suppressed on further calls
For your first two example cases, you could use key() and current() to assign the values you need.
$ar = $o->me; // reset isn't necessary, since you just created the array
$typ = key($ar);
$val = current($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = [key($out), current($out)];
In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.
For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.
foreach ($broken as $k => $v) {
$kv = [$k, $v];
}
For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.
$this->result = current($this->cache_data);
Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.
Fifth can be replaced with a for() loop.
reset($array);
for ($i = 0; $i < 30; $i++) {
$id = key($array);
$item = current($array);
// code
next($array);
}
2019+ Instant Upgrade of each()
There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.
-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}
And:
-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}
You can replace one by one manually. But isn't there a better way?
I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).
It's part of the PHP_72 set.
4 Steps to Upgrade your Code
1. Install it
composer require rector/rector --dev
2. Create rector.php config
vendor/bin/rector init
3. Add PHP_72 set
<?php
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};
4. Run it on your code
vendor/bin/rector process src --set php72
I hope it helps you with your migration.
If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.
I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().
Case 1: Missing $value
reset($array);
while (list($key, ) = each($array)) {
Update to:
foreach(array_keys($array) as $key) {
Case 2: Missing $key
reset($array);
while (list(, $value) = each($array)) {
Update to:
foreach($array as $value) {
Case 3: Not missing anything
reset($array);
while (list($key, $value) = each($array)) {
Update to:
foreach($array as $key => $value) {
you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:
<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
1.
$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);
2.
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);
3.
for(reset($broken);$kv = myEach($broken);) {...}
reset($array);
while (list($key, $value) = each($array)) {
UPDATE
reset($array);
foreach($array as $key => $value) {
Here are some ways to do it:
The standard foreach loop (very readable):
foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}
Or, reducing:
$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});
Or, in a functional expression:
$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));
None of these methods need reset($this->contents); preceding it.
The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini
auto_prepend_file = "/var/www/php/auto_prepend.php"
Then make the file and enter in the function with an function_exists wrapper.
<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}
This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.
This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.
When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.
I used Wee Zel's emulation of the each function
To expand on Petro Mäntylä excellent correct answer for Case 3:
Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:
This is genuine code from a 3rd party old code base (TCPDF)
DEPRECATED:
while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}
FIXED:
// while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);
Replace this code
while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
// if ($_resourceTypeNode['name'] === $resourceTypeName) {
// $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
// return $this->_resourceTypeConfigCache[$resourceTypeName];
// }
// }
with this one
foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}
// while (list($products_id, ) = each($this->contents)) {
// $total_items += $this->get_quantity($products_id);
// }
Update To :
foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}
Other Condition:
foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}
For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.
What about using this function?
function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}
I need to use the SoapClient PHP function to use a service from another site that will give me a certain information.
Can they know from what site came that request?
Thank you all.
Yes, if you put this code (example)
function get_base_domain_by_name ( $name ) {
if (is_string($name) && preg_match('/^[a-z0-9\-\.]+\.[a-z0-9\-]+$/i', $name) === 1) {
$parts = array_reverse(explode('.', strtolower($name)));
$base = array_shift($parts);
foreach ($parts as $part ) {
$base = $part.'.'.$base;
if (( $addr = gethostbyname($base) ) != $base && preg_match('/^[0-9\.]+$/', $addr) === 1 ) {
return($base);
}
}
}
return(false);
}
function get_base_domain_by_addr ( $addr ) {
return(get_base_domain_by_name(gethostbyaddr($addr)));
}
$domain = get_base_domain_by_addr($_SERVER['REMOTE_ADDR']);
echo $domain;
You can get the URL of the of the Site that accessed you, and if you use: $_SERVER['REMOTE_ADDR'] you can get the IP Address.
My host suddenly changed something , and now my sites ( most wp - around 100 ) are getting the infamous error Invalid opcode 153/1/8
The line(s) responsible for it :
$f = function() use ($out) {
echo $out;
};
After 2 minutes of research it appears that the culprit is eAccelerator , which does not support anonymous functions
Both the following questions blamed the error on eAccelerator as well :
Invalide OpCode and php sort function,
https://stackoverflow.com/a/12085901/1244126
Fun fact : the same code was already before a subject of my own 2 questions here on SE and here , where I encountered a problem while using
anonymous functions with older PHP versions ( < 5.3 ) with create_function
$f = create_function(' $out ',' global $out; echo $out;');
So, my question is : how can I change my code in a manner that will avoid the eAccelerator bug AND will work on all php versions . ( it is unlikely that I can convince my host to change something on it´s side )
EDIT I :
For sake of clarity ( although might be slightly irrelevant - the the question is how to have a cross-compatible anonymous functions ) - I am posting the whole relevant code ...
if ( count( $content_widget ) > 0 ) { // avoid error when no widget...
$i=0;
foreach ( $content_widget as $wid ){
$out = null;
$i++;
$widg_id = 'o99_dashboard_widget_dyn_' . $i;
$widg_name = 'widget name - ' . $i;
$out = $wid;
// $f = create_function('$out','global $out;echo $out;');
// $f = create_function('', 'global $out; echo $out ;');
$f = function() use ($out) {
echo $out;
};
// function() use ($out) // NOPE
// $f = $f($out); // NOPE again
wp_add_dashboard_widget($widg_id, $widg_name, $f);
// $i++;
}
}
It is just a simple code to dynamically create dashboard widgets in wp admin area..
It seems that they are using call_user_func
So, you could create new object and pass callable array.
class s {
private $_out = null;
public function __construct($out){
$this->_out = $out;
}
public function a(){
echo $this->_out;
}
}
$function = array(new S('my out'), 'a');
var_dump(is_callable($function));
call_user_func($function);
I am GETting the url test.php?value=%22hello%22 and when I print out the value it shows \"hello\" and $_REQUEST['value'][0] is \. Why? How do I fix this (correctly)?
The most likely reason is that you have magic quotes turned on. You should:
Upgrade to PHP 5.4 or
disable them in your PHP configuration file or
disable them in your Apache configuration (same link) or
(last resort) test to see if they are turned on and then run stripslashes over the input.
If you can't guarantee the environment to allow reconfiguration, you could use this reusable code to recursively dig through $_GET, $_POST arrays and clean them up with stripslashes:
class de_slasher {
function recursive_stripslashes($a) {
$b = array();
foreach( $a as $k => $v ) {
$k = stripslashes($k);
if( is_array($v) ) {
$b[$k] = $this->recursive_stripslashes($v);
} else {
$b[$k] = stripslashes($v);
}
}
return($b);
}
function check_and_fix_magic_quotes( &$array ) {
if( get_magic_quotes_gpc() ) {
$array = $this->recursive_stripslashes( $array );
}
}
function __construct( $auto = false ) {
if( $auto === true ) {
$this->check_and_fix_magic_quotes( $_POST );
$this->check_and_fix_magic_quotes( $_GET );
}
}
}
To use, simply include the class, and invoke $slasher = new de_slasher(true); to automatically clean up $_GET and $_POST. This only happens if magic quotes setting is on. If you instantiate the class without the 'true' parameter, then you can selectively deep-filter any array:
$my_array = array( "name" => "Herbert\'s Apple" );
$slasher = new de_slasher();
$slasher->check_and_fix_magic_quotes( $my_array );
Is there a way to know the avaliable ram in a server (linux distro) with php (widthout using linux commands)?
edit: sorry, the objective is to be aware of the ram available in the server / virtual machine, for the particular server (even if that memory is shared).
If you know this code will only be running under Linux, you can use the special /proc/meminfo file to get information about the system's virtual memory subsystem. The file has a form like this:
MemTotal: 255908 kB
MemFree: 69936 kB
Buffers: 15812 kB
Cached: 115124 kB
SwapCached: 0 kB
Active: 92700 kB
Inactive: 63792 kB
...
That first line, MemTotal: ..., contains the amount of physical RAM in the machine, minus the space reserved by the kernel for its own use. It's the best way I know of to get a simple report of the usable memory on a Linux system. You should be able to extract it via something like the following code:
<?php
$fh = fopen('/proc/meminfo','r');
$mem = 0;
while ($line = fgets($fh)) {
$pieces = array();
if (preg_match('/^MemTotal:\s+(\d+)\skB$/', $line, $pieces)) {
$mem = $pieces[1];
break;
}
}
fclose($fh);
echo "$mem kB RAM found"; ?>
(Please note: this code may require some tweaking for your environment.)
Using /proc/meminfo and getting everything into an array is simple:
<?php
function getSystemMemInfo()
{
$data = explode("\n", file_get_contents("/proc/meminfo"));
$meminfo = array();
foreach ($data as $line) {
list($key, $val) = explode(":", $line);
$meminfo[$key] = trim($val);
}
return $meminfo;
}
?>
var_dump( getSystemMemInfo() );
array(43) {
["MemTotal"]=>
string(10) "2060700 kB"
["MemFree"]=>
string(9) "277344 kB"
["Buffers"]=>
string(8) "92200 kB"
["Cached"]=>
string(9) "650544 kB"
["SwapCached"]=>
string(8) "73592 kB"
["Active"]=>
string(9) "995988 kB"
...
Linux commands can be run using the exec function in PHP. This is efficient and will do the job(if objective is to get the memory).
Try the following code:
<?php
exec("free -mtl", $output);
print_r($output);
?>
Small and tidy function to get all of its values associated to their keys.
$contents = file_get_contents('/proc/meminfo');
preg_match_all('/(\w+):\s+(\d+)\s/', $contents, $matches);
$info = array_combine($matches[1], $matches[2]);
// $info['MemTotal'] = "2047442"
I don't think you can access the host server memory info without a special written PHP extension. The PHP core library does not allow (perhaps for security reasons) to access the extended memory info.
However, if your script has access to the /proc/meminfo then you can query that special file and grab the info you need. On Windows (although you've not asked for it) we can use the com_dotnet PHP extension to query the Windows framework via COM.
Below you can find my getSystemMemoryInfo that returns that info for you no matter if you run the script on a Linux/Windows server. The wmiWBemLocatorQuery is just a helper function.
function wmiWBemLocatorQuery( $query ) {
if ( class_exists( '\\COM' ) ) {
try {
$WbemLocator = new \COM( "WbemScripting.SWbemLocator" );
$WbemServices = $WbemLocator->ConnectServer( '127.0.0.1', 'root\CIMV2' );
$WbemServices->Security_->ImpersonationLevel = 3;
// use wbemtest tool to query all classes for namespace root\cimv2
return $WbemServices->ExecQuery( $query );
} catch ( \com_exception $e ) {
echo $e->getMessage();
}
} elseif ( ! extension_loaded( 'com_dotnet' ) )
trigger_error( 'It seems that the COM is not enabled in your php.ini', E_USER_WARNING );
else {
$err = error_get_last();
trigger_error( $err['message'], E_USER_WARNING );
}
return false;
}
// _dir_in_allowed_path this is your function to detect if a file is withing the allowed path (see the open_basedir PHP directive)
function getSystemMemoryInfo( $output_key = '' ) {
$keys = array( 'MemTotal', 'MemFree', 'MemAvailable', 'SwapTotal', 'SwapFree' );
$result = array();
try {
// LINUX
if ( ! isWin() ) {
$proc_dir = '/proc/';
$data = _dir_in_allowed_path( $proc_dir ) ? #file( $proc_dir . 'meminfo' ) : false;
if ( is_array( $data ) )
foreach ( $data as $d ) {
if ( 0 == strlen( trim( $d ) ) )
continue;
$d = preg_split( '/:/', $d );
$key = trim( $d[0] );
if ( ! in_array( $key, $keys ) )
continue;
$value = 1000 * floatval( trim( str_replace( ' kB', '', $d[1] ) ) );
$result[$key] = $value;
}
} else // WINDOWS
{
$wmi_found = false;
if ( $wmi_query = wmiWBemLocatorQuery(
"SELECT FreePhysicalMemory,FreeVirtualMemory,TotalSwapSpaceSize,TotalVirtualMemorySize,TotalVisibleMemorySize FROM Win32_OperatingSystem" ) ) {
foreach ( $wmi_query as $r ) {
$result['MemFree'] = $r->FreePhysicalMemory * 1024;
$result['MemAvailable'] = $r->FreeVirtualMemory * 1024;
$result['SwapFree'] = $r->TotalSwapSpaceSize * 1024;
$result['SwapTotal'] = $r->TotalVirtualMemorySize * 1024;
$result['MemTotal'] = $r->TotalVisibleMemorySize * 1024;
$wmi_found = true;
}
}
// TODO a backup implementation using the $_SERVER array
}
} catch ( Exception $e ) {
echo $e->getMessage();
}
return empty( $output_key ) || ! isset( $result[$output_key] ) ? $result : $result[$output_key];
}
Example on a 8GB RAM system
print_r(getSystemMemoryInfo());
Output
Array
(
[MemTotal] => 8102684000
[MemFree] => 2894508000
[MemAvailable] => 4569396000
[SwapTotal] => 4194300000
[SwapFree] => 4194300000
)
If you want to understand what each field represent then read more.
It is worth noting that in Windows this information (and much more) can be acquired by executing and parsing the output of the shell command: systeminfo
exec("grep MemTotal /proc/meminfo", $aryMem);
$aryMem[0] has your total ram minus kernel usage.
I don't remember having ever seen such a function -- its kind of out the scope of what PHP is made for, actually.
Even if there was such a functionnality, it would probably be implemented in a way that would be specific to the underlying operating system, and wouldn't probably work on both Linux and windows (see sys_getloadavg for an example of that kind of thing)
// helpers
/**
* #return array|null
*/
protected function getSystemMemInfo()
{
$meminfo = #file_get_contents("/proc/meminfo");
if ($meminfo) {
$data = explode("\n", $meminfo);
$meminfo = [];
foreach ($data as $line) {
if( strpos( $line, ':' ) !== false ) {
list($key, $val) = explode(":", $line);
$val = trim($val);
$val = preg_replace('/ kB$/', '', $val);
if (is_numeric($val)) {
$val = intval($val);
}
$meminfo[$key] = $val;
}
}
return $meminfo;
}
return null;
}
// example call to check health
public function check() {
$memInfo = $this->getSystemMemInfo();
if ($memInfo) {
$totalMemory = $memInfo['MemTotal'];
$freeMemory = $memInfo['MemFree'];
$swapTotalMemory = $memInfo['SwapTotal'];
$swapFreeMemory = $memInfo['SwapFree'];
if (($totalMemory / 100.0) * 30.0 > $freeMemory) {
if (($swapTotalMemory / 100.0) * 50.0 > $swapFreeMemory) {
return new Failure('Less than 30% free memory and less than 50% free swap space');
}
return new Warning('Less than 30% free memory');
}
}
return new Success('ok');
}