1

I developed this function which reads N lines from a file and outputs them.

public function readLinesFromFile($file, $maxLines, $reverse=false)
{
    $lines = file($file);

    if ($lines == false) {
        echo "Datei '$file' konnte nicht geöffnet werden.";
        return false;
    }
    if ($maxLines > count($lines)) {
        echo "\$maxLines ist größer als die Anzahl der Zeilen in der Datei.";
        return false;
    }

    if ($reverse) {
        $lines = array_reverse($lines);
    }

    $tmpArr = array();

    for ($i=0; $i < $maxLines; $i++) {
        array_push($tmpArr, $lines[$i]);
    }

    if ($reverse) {
        $tmpArr = array_reverse($tmpArr);
    }

    $out = "";
    for ($i=0; $i < $maxLines; $i++) {
        $out .= $tmpArr[$i] . "</br>";
    }

    return $out;
}

Everything worked fine, but now of a sudden I get:

Warning: file(C:_Projekte\selenium.env): failed to open stream: Too many open files in C:_Projekte\selenium\vendor\vlucas\phpdotenv\src\Loader.php on line 135

I am using the php method file. It does not return a handle but an array. I am not sure wether the handle is closed by the function or not. I can only see its declaration:

function file(string $filename, int $flags = 0, $context = null): array {}

Is this a PHP Bug or my fault? I know I can increase the limit of open files, but this is not a solution. Should I use another method than file()?


I am fetching the last 100 Lines of a logfile in a specific interval (each 250 ms). I guess the handles are opened but not closed by file()

Black
  • 18,150
  • 39
  • 158
  • 271
  • Try to use `file_get_contents` – Nikita Leshchev Jul 18 '18 at 14:23
  • 1
    Why do you think that your method causes error? As I can see error happens in vendor library. – u_mulder Jul 18 '18 at 14:23
  • 1
    `C:_Projekte\selenium\vendor\vlucas\phpdotenv\src\Loader.php ` -> doesn't seem to have anything to do with your file. You did hit open files limit, for some reason, but your method doesn't seem to have anything to do with it. – N.B. Jul 18 '18 at 14:24
  • Can you give a bit more context about what how you interact with `Loader.php` and how you are trying to load `selenium.env`? – Decent Dabbler Jul 18 '18 at 14:24
  • problem is probably in the loop/iterator that calls this method a hundred times – Andrew Jul 18 '18 at 14:26
  • I am fetching the last 100 Lines of a logfile in a specific interval (each 250 ms). I guess the handles are opened but not closed by `file()` – Black Jul 18 '18 at 14:27
  • Why are you reversing the order of the lines twice, which cancels each other out? Also why not use `join( '
    ', $tmpArray)` to join the data at the end
    – vogomatix Jul 18 '18 at 14:29
  • You would be much better off looking at `array_slice()` to extract lines from the array than the way your trying to do it. – Nigel Ren Jul 18 '18 at 14:29
  • For your last loop - look at `implode()`. – Nigel Ren Jul 18 '18 at 14:30
  • @vogomatix, it does not cancel each other out. It is needed and works perfectly fine like this. – Black Jul 18 '18 at 14:31
  • Apart from the problem, your method is inefficient. Refer to [this StackOverflow answer](https://stackoverflow.com/a/43634225/693806) to see how to efficiently get last 100 lines from file. – N.B. Jul 18 '18 at 14:32
  • @N.B. did you measured both? The one you postet looks more complicated than mine. – Black Jul 18 '18 at 14:33
  • 1
    `file()` [does close the file handle automatically.](https://stackoverflow.com/a/29060966) – Decent Dabbler Jul 18 '18 at 14:34
  • @Black - I have. It contains more code, but your method loads *entire* file into an array opposed to the other one which reads only `n` lines starting at the end. If your file has 1GB, `file` method will attempt to load entire gigabyte into an array. That's where your approach fails. – N.B. Jul 18 '18 at 14:48
  • @N.B. ok thanks this makes sense. – Black Jul 18 '18 at 14:49

1 Answers1

1

I've improved the original method with commented out original code...

public function readLinesFromFile($file, $maxLines, $reverse=false)
{
$lines = file($file);

if ($lines == false) {
    echo "Datei '$file' konnte nicht geöffnet werden.";
    return false;
}
if ($maxLines > count($lines)) {
    echo "\$maxLines ist größer als die Anzahl der Zeilen in der Datei.";
    return false;
}

/*
 *** NONE OF THIS IS NEEDED ***

// array_splice better, removes double reverse...
if ($reverse) {
    $lines = array_reverse($lines);
}

// push loop not needed, just use array_splice...
$tmpArr = array();

for ($i=0; $i < $maxLines; $i++) {
    array_push($tmpArr, $lines[$i]);
}

// reverse not needed as array_splice solves this too..
if ($reverse) {
    $tmpArr = array_reverse($tmpArr);
}

// join instead of concatenate loop
$out = "";
for ($i=0; $i < $maxLines; $i++) {
    $out .= $tmpArr[$i] . "</br>";
}

return $out;

*/

$tmpArr = $reverse ?
    array_slice( $lines, -$maxLines) :
    array_slice( $lines, 0, $maxLines);

// don't even need $tmpArr local but will leave it in
return join( '</br>', $tmpArr);


}
vogomatix
  • 4,856
  • 2
  • 23
  • 46
  • 2
    I think as they want the last N lines, the first reverse makes the end of the file at the start, it extracts the first N lines and then the second reverse puts them in the correct order. Instead could do `$tmpArr = array_slice( $lines, -$maxLines);` - I think! – Nigel Ren Jul 18 '18 at 14:44
  • Yep I did calculate the number of lines, but using a negative value is better, so amended answer – vogomatix Jul 18 '18 at 14:46
  • @vogomatix, thanks for your improved version, very nice and helpful! I tested it and it still works as expected. – Black Jul 18 '18 at 14:51
  • 1
    Amazing what you can do whilst waiting for a repository to clone :) – vogomatix Jul 18 '18 at 14:53