aleation
aleation

Reputation: 4834

SplFileObject move pointer to previous line

So I am doing a small experiment for myself, a script to read the php errors' log file (with SplFileObject) and output it formatted on the browser.

I though it would be more logic to display it in reversed order (the latest errors on top). To use the "normal" order I would just display each line and call $file->next(); to move the pointer, but as I'm doing it the other way around, and there's not a prev() or previous() method as far as I know, the only way I found was using seek():

for($i = $lines_total - $start_at; $i > $lines_total - $start_at - $lines_to_get; $i--){
    $content->seek($i);
    $data = $content->current();
    if(empty($data)){
        continue;
    }
}

But this is incredibly slow (around 7 Secs for a 16mb file). If I do it in the normal order it's instant.

Do anyone knows any method? or what I'm trying to do is crazy? xD I'm just a designer forced to code, so I am not very familiarized with pointers and stuff like that.

Upvotes: 2

Views: 1949

Answers (3)

ArtisticPhoenix
ArtisticPhoenix

Reputation: 21671

This is an old question but with SplFileObj the simplest way is to use the key and seek,

public function prev(){
    $key = $this->_splFileObj->key();
    if( $key ){
        --$key;
    }
    $this->_splFileObj->seek($key);
}

Assuming this method is in a wrapper around SplFileObj with the property of _splFileObj.

Upvotes: 0

aleation
aleation

Reputation: 4834

In case anybody encounters this problem in the future I came up with a quite simple solution:

//get to the last position of the file and get the pointer position.
$content->seek($content->getSize());
$lines_total = $content->key();
$byte = $content->ftell();

//all the line in the php error log starts like: [21-Feb-2013 22:34:53 UTC] so...
$pattern = '/^\[.*\]/';

for(...){
//get the current output to preg_match it
    $data = $content->current();

//go backward each time it doesnt match a line's start
    while ( preg_match( $pattern, $data ) === 0 ){
    $byte--;
    $content->fseek($byte);
    $data = $content->current();
    }

//go backward 1 more position to start the next loop
    $byte--;
    $content->fseek($byte);
}

Hope this helps someone someday xD

Upvotes: 2

Baba
Baba

Reputation: 95131

FROM PHP DOC

prev — Rewind the internal array pointer
prev() behaves just like next(), except it rewinds the internal array pointer one place instead of advancing it.

As you can see they only work with arrays not file pointer .... you can only use it like this

$file = new SplFileObject("log.txt", "r");
$file  = iterator_to_array($file);

echo current($file), PHP_EOL;
echo next($file), PHP_EOL;
echo prev($file), PHP_EOL;

If you want to move to the next line you can try using SplFileObject::ftell to get the previous position then use SplFileObject::fseek to implement your reverse ...

Example

$file = new ReverseableSplFileObject("log.txt", "r");
foreach ( $file as $c ) {
    echo $c, PHP_EOL;
}
echo $file->prev(), PHP_EOL;
echo $file->prev(), PHP_EOL;
echo $file->prev(), PHP_EOL;

Output

A
B
C
C
B
A

Modified class

class ReverseableSplFileObject extends SplFileObject {
    private $pos = array();

    function current() {
        return trim(parent::current());
    }

    function next() {
        $this->eof() or $this->pos[] = $this->ftell();
        return parent::next();
    }

    function prev() {
        if (empty($this->pos)) {
            $this->fseek(0);
        } else {
            $this->fseek(array_pop($this->pos));
        }
        return $this->current();
    }
}

Upvotes: 2

Related Questions