5
\$\begingroup\$

In try to replicate the outcome of the following command sequence using php:

netstat -rn | grep "^0.0.0.0 " | cut -d " " -f10

I did this using a PHP script:

#!/usr/bin/env php

<?php

/**
 *  Xdebug ip detector for docker
 *  Copyright (C) 2023  Dimitrios Desyllas

 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.

 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

$matches = [];
preg_match("/^\w*\s(00000000)\s(\w*)/m",file_get_contents("/proc/net/route"),$matches);

// At regex each () is also matxched seperately. In my case I have 2 matched groups therefore the secodn match is my ip hex encoded
$ip = $matches[2];
$ip = str_split($ip,2);
$ip = array_reverse($ip);

$ip = array_reduce($ip,function($acc,$item){
    return $acc.intval($item,16).".";
},"");

$ip = rtrim($ip,'.');

echo $ip;

Is this the way to parse the ip directly from /proc/net/route? This script is intended to be run inside a docker container that also ships with PHP and xdebug.

\$\endgroup\$

2 Answers 2

5
\$\begingroup\$

Reading the gateway IP

I'm not an expert of reading routing info, but:

So I think your method is fine.

Another alternative to reading from /proc/net/route would be to call the netstat command as you mention in your post and parse its output.

Reading the gateway IP as a hex string

This functionality seems a good candidate to wrap into a function, having this "shape":

# Returns the gateway IP address as a hex string of reversed bytes,
# for example 0100A8C0, or null if not possible
function read_gateway_ip_hex_string() {
  # ...
}

I find the preg_match solution a bit hacky. I find it more natural to consider the structure of the file as a table with columns, and get the 2nd column when the 1st column is 00000000:

function read_gateway_ip_hex_string() {
  $fn = fopen("/proc/net/route", "r");
  
  while(! feof($fn))  {
    $line = fgets($fn);
    $parts = preg_split("/\s+/", $line);
    if ($parts[1] == "00000000") {
      $ip_hex_string = $parts[2];
      break;
    }
  }
 
  fclose($fn);

  return $ip_hex_string;
}

It's more verbose than the posted code, but I think it's easier to understand how it works.

Converting a hex string to dotted decimals

Similarly to getting the hex string, I think this is also a good opportunity to wrap this functionality into a reusable and testable function:

function ip_hex_string_to_dotted_decimals($ip_hex_string) {
    $ip_bytes = array_reverse(
        array_map(fn($s): int => intval($s, 16),
            str_split($ip_hex_string, 2)));

    return implode('.', $ip_bytes);
}

Instead of the array_reduce + rtrim combo, this one maps array items and then joins them, which I find easier to understand.

Putting it together

From the building blocks in functions, the main part of the program becomes:

$ip_hex_string = read_gateway_ip_hex_string();

if (is_null($ip_hex_string)) {
    # TODO: do something helpful!
} else {
    $ip = ip_hex_string_to_dotted_decimals($ip_hex_string);
    echo $ip;
}
\$\endgroup\$
1
  • \$\begingroup\$ well I wanted to use as a small script and not part of a major app in my case. Also docker container routes are small. \$\endgroup\$ Sep 13, 2023 at 20:44
3
\$\begingroup\$

It is wise to ensure an array has a key before dereferencing it

After the call to preg_match() the code accesses key 2:

$ip = $matches[2];

While it may be improbable, it may be plausible that there is no value at index 2 and in that scenario a warning would be thrown:

Warning: Undefined array key 2

It is a good habit to ensure to guard against cases where an array doesn't have a key - perhaps with conditional logic, null coalescing operator or a null-safe operator.

Follow PHP Standards Recommendations

While there are no de jure rules about readability conventions and it is up to individuals/teams to decide their conventions, idiomatic PHP code often follows the PHP Standards Recommendations - e.g. PSR-12: Extended Coding Style.

$ip = str_split($ip,2);

This goes against PSR-12 section 4.5:

4.5 Method and Function Arguments

In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.

$ip = str_split($ip, 2);

And

return $acc.intval($item,16).".";

This goes against PSR-12 section 6.2:

6.2. Binary operators

All binary arithmetic, comparison, assignment, bitwise, logical, string, and type operators MUST be preceded and followed by at least one space:

Binary operators are typically separated with a space on both sides:

return $acc . intval($item,16) . ".";

Variable is over-written

There are five assignments to $ip:

$ip = $matches[2];
$ip = str_split($ip,2);
$ip = array_reverse($ip);

$ip = array_reduce($ip,function($acc,$item){
    return $acc.intval($item,16).".";
},"");

$ip = rtrim($ip,'.');

Over-writing $ip each time prevents re-using a value, which may not be a problem, though the meaning and type of the variable would change. For readability it would be better to use different names for each assignment.

\$\endgroup\$
1
  • \$\begingroup\$ So for the $ip = $matches[2]; what approach would you reccomend? \$\endgroup\$ Sep 14, 2023 at 6:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.