<?php
 
/**
 
 *    This package contains four functions to be used for inserting
 
 *    or removing portions of a file mid-stream while reducing memory 
 
 *    consumption by using a temp file instead for storing data in memory
 
 *
 
 *    These functions are best when used with large files that cause 
 
 *    memory capacity to be exceeded
 
 *
 
 *    @author Sam Shull <sam.shull@jhspecialty.com>
 
 *    @version 1.0
 
 *
 
 *    @copyright Copyright (c) 2009 Sam Shull <sam.shull@jhspeicalty.com>
 
 *    @license <http://www.opensource.org/licenses/mit-license.html>
 
 *
 
 *    Permission is hereby granted, free of charge, to any person obtaining a copy
 
 *    of this software and associated documentation files (the "Software"), to deal
 
 *    in the Software without restriction, including without limitation the rights
 
 *    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
 *    copies of the Software, and to permit persons to whom the Software is
 
 *    furnished to do so, subject to the following conditions:
 
 *    
 
 *    The above copyright notice and this permission notice shall be included in
 
 *    all copies or substantial portions of the Software.
 
 *    
 
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
 *    THE SOFTWARE.
 
 *
 
 */
 
 
/**
 
 *    Insert a string into a file at the current position
 
 *    of the file pointer
 
 *
 
 *    @param resource $fp
 
 *    @param string $str
 
 *    @param integer $length - maximum length to write to file
 
 *
 
 *    @return integer
 
 */
 
function finsert ($fp, $str, $length=null)
 
{
 
    $ret = 0;
 
    $length = !is_null($length) ? (int)$length : strlen($str) + 1;
 
    
 
    //flush all the written data
 
    fflush($fp);
 
    
 
    //track the current position
 
    $current = ftell($fp);
 
    
 
    //open a temp file for the rest of the data in the current file
 
    $temp = tmpfile();
 
    
 
    //copy the rest of the data in the file to a temp file
 
    //stream_copy_to_stream($fp, $temp, -1, $current);
 
    while (!feof($fp))
 
    {
 
        fwrite($temp, fread($fp, 4096));
 
    }
 
    
 
    //rewind the temp file
 
    rewind($temp);
 
    
 
    //rewind the file pointer to the current position
 
    //using truncate allows the use of a+
 
    ftruncate($fp, $current);
 
    
 
    //write in the text
 
    $ret += fwrite($fp, $str, $length);
 
    
 
    //copy all the data back in
 
    //stream_copy_to_stream($temp, $fp);
 
    while (!feof($temp))
 
    {
 
        fwrite($fp, fread($temp, 4096));
 
    }
 
    
 
    //move the cursor to the end of the inserted text
 
    fseek($fp, $current + $ret, SEEK_SET);
 
    
 
    //get rid of the temp file
 
    ftruncate($temp, 0);
 
    fclose($temp);
 
    
 
    //make sure to flush
 
    fflush($fp);
 
    
 
    return $ret;
 
}
 
 
/**
 
 *    Insert an array as a csv line into a file at the current position
 
 *    of the file pointer
 
 *
 
 *    @param resource $fp
 
 *    @param array $fields
 
 *    @param string $delimiter = ,
 
 *    @param string $enclosure = "
 
 *    @param string $escape = \
 
 *
 
 *    @return integer
 
 */
 
function finsertcsv ($fp, array $fields, $delimiter=",", $enclosure='"', $escape="\\")
 
{
 
    $func = create_function('$a', '
 
                                    $b=\''.str_replace("'", "\'", $enclosure).'\';
 
                                    $c=\''.str_replace("'", "\'", $escape . ($escape == "\\" ? $escape : "")).'\';
 
                                    return $b . str_replace($b, $c . $b, $a) . $b;
 
                                ');
 
    
 
    $str = implode(
 
                $delimiter, 
 
                array_map(
 
                    $func, 
 
                    $fields
 
                )
 
            );
 
    return finsert($fp, $str . PHP_EOL);
 
}
 
 
/**
 
 *    Remove a portion of a file with a given length
 
 *    at the current position of the file pointer
 
 *
 
 *    @param resource $fp
 
 *    @param integer $length
 
 *
 
 *    @return boolean
 
 */
 
function fremove ($fp, $length)
 
{
 
    $ret = false;
 
    
 
    //flush all the written data
 
    fflush($fp);
 
    
 
    //track the current position
 
    $current = ftell($fp);
 
    
 
    //move the cursor to the desired position
 
    fseek($fp, $length, SEEK_CUR);
 
    
 
    //open a temp file for the rest of the data in the current file
 
    $temp = tmpfile();
 
    
 
    //copy the rest of the data in the file to a temp file
 
    //stream_copy_to_stream($fp, $temp, -1, $current);
 
    while (!feof($fp))
 
    {
 
        fwrite($temp, fread($fp, 4096));
 
    }
 
    
 
    //rewind the temp file
 
    rewind($temp);
 
    
 
    //rewind the file pointer to the current position
 
    //using truncate allows the use of a+
 
    $ret = ftruncate($fp, $current);
 
    
 
    //copy all the data back in
 
    //stream_copy_to_stream($temp, $fp);
 
    while (!feof($temp))
 
    {
 
        fwrite($fp, fread($temp, 4096));
 
    }
 
    
 
    //move the cursor to the end of the inserted text
 
    fseek($fp, $current, SEEK_SET);
 
    
 
    //get rid of the temp file
 
    ftruncate($temp, 0);
 
    fclose($temp);
 
    
 
    //make sure to flush
 
    fflush($fp);
 
    
 
    return $ret;
 
}
 
 
/**
 
 *    Remove a csv line from a file at the current position
 
 *    of the file pointer
 
 *
 
 *    @param resource $fp
 
 *    @param array $fields
 
 *    @param string $delimiter = ,
 
 *    @param string $enclosure = "
 
 *
 
 *    @return integer
 
 */
 
function fremovecsv ($fp, $length=0, $delimiter=",", $enclosure='"', $escape="\\")
 
{
 
    $current = ftell($fp);
 
    version_compare(PHP_VERSION, "5.3", ">=") ? 
 
        fgetcsv($fp, $length, $delimiter, $enclosure, $escape) :
 
        fgetcsv($fp, $length, $delimiter, $enclosure);
 
    $now = ftell($fp);
 
    fseek($fp, $current, SEEK_SET);
 
    return fremove($fp, $now - $current);
 
}
 
 
/**
 
 *    Insert a string at the beginning of a file
 
 *
 
 *    @param resource $fp
 
 *    @param string $str
 
 *    @param integer $length - maximum length to write to file
 
 *
 
 *    @return integer
 
 */
 
function fprepend ($fp, $str, $length=null)
 
{
 
    $ret = 0;
 
    $length = !is_null($length) ? (int)$length : strlen($str) + 1;
 
    
 
    //flush all the written data
 
    fflush($fp);
 
    
 
    //track the current position
 
    $current = ftell($fp);
 
    rewind($fp);
 
    
 
    //open a temp file for the rest of the data in the current file
 
    $temp = tmpfile();
 
    
 
    //copy the rest of the data in the file to a temp file
 
    //stream_copy_to_stream($fp, $temp, -1, $current);
 
    while (!feof($fp))
 
    {
 
        fwrite($temp, fread($fp, 4096));
 
    }
 
    
 
    //rewind the temp file
 
    rewind($temp);
 
    
 
    //rewind the file pointer to the current position
 
    //using truncate allows the use of a+
 
    ftruncate($fp, 0);
 
    
 
    //write in the text
 
    $ret += fwrite($fp, $str, $length);
 
    
 
    //copy all the data back in
 
    //stream_copy_to_stream($temp, $fp);
 
    while (!feof($temp))
 
    {
 
        fwrite($fp, fread($temp, 4096));
 
    }
 
    
 
    //move the cursor to the end of the inserted text
 
    fseek($fp, $current + $ret, SEEK_SET);
 
    
 
    //get rid of the temp file
 
    ftruncate($temp, 0);
 
    fclose($temp);
 
    
 
    //make sure to flush
 
    fflush($fp);
 
    
 
    return $ret;
 
}
 
 
/**
 
 *    Insert a string into a file at a specific distance from a given position
 
 *
 
 *    @param resource $fp
 
 *    @param string $str
 
 *    @param integer $offset
 
 *    @param integer $length
 
 *    @param integer $whence = SEEK_SET
 
 *
 
 *    @return integer
 
 */
 
function fchange ($fp, $str, $offset, $length, $whence=SEEK_SET)
 
{
 
    $ret = 0;
 
    
 
    //flush all the written data
 
    fflush($fp);
 
    
 
    fseek($fp, $offset, $whence);
 
    fseek($fp, $length, SEEK_CUR);
 
    
 
    //open a temp file for the rest of the data in the current file
 
    $temp = tmpfile();
 
    
 
    //copy the rest of the data in the file to a temp file
 
    //stream_copy_to_stream($fp, $temp, -1, $current);
 
    while (!feof($fp))
 
    {
 
        fwrite($temp, fread($fp, 4096));
 
    }
 
    
 
    //rewind the temp file
 
    rewind($temp);
 
    
 
    //rewind the file pointer to the current position
 
    //using truncate allows the use of a+
 
    ftruncate($fp, $offset);
 
    
 
    //write in the text
 
    $ret += fwrite($fp, $str, $length);
 
    
 
    //copy all the data back in
 
    //stream_copy_to_stream($temp, $fp);
 
    while (!feof($temp))
 
    {
 
        fwrite($fp, fread($temp, 4096));
 
    }
 
    
 
    //move the cursor to the end of the inserted text
 
    fseek($fp, $offset, $whence);
 
    fseek($fp, $length, SEEK_CUR);
 
    
 
    //get rid of the temp file
 
    ftruncate($temp, 0);
 
    fclose($temp);
 
    
 
    //make sure to flush
 
    fflush($fp);
 
    
 
    return $ret;
 
}
 
 
/**
 
 *    Find an occurrence of a PCRE pattern in the next line from 
 
 *    a given stream resource - used the same way fscanf would be used
 
 *
 
 *    @param resource $fp
 
 *    @param string $regex
 
 *    @param array $matches = null
 
 *    @param integer $flags = 0
 
 *    @param integer $offset = 0
 
 *
 
 *    @return integer
 
 */
 
function fpreg_match($fp, $regex, &$matches=null, $flags=0, $offset=0)
 
{
 
    //if feof return 0 matches, else look for the pattern in the next line of the stream
 
    return feof($fp) ? 0 : preg_match($regex, fgets($fp), $matches, $flags, $offset);
 
}
 
 
/**
 
 *    Find all of the occurrences of a PCRE pattern in a file
 
 *    from the given pointer position
 
 *
 
 *    @param resource $fp
 
 *    @param string $regex
 
 *    @param array $matches = null
 
 *    @param integer $flags = 0
 
 *
 
 *    @return integer
 
 */
 
function fpreg_match_all($fp, $regex, &$matches=null, $flags=0)
 
{
 
    $pos = ftell($fp);
 
    
 
    $matched = 0;
 
    $match = null;
 
    $matches = array();
 
    
 
    $flag = $flags & PREG_OFFSET_CAPTURE;
 
    
 
    while (!feof($fp))
 
    {
 
        if (preg_match($regex, fgets($fp), $match, $flag))
 
        {
 
            ++$matched;
 
            $matches[] = $match;
 
        }
 
    }
 
    
 
    fseek($fp, $pos, SEEK_SET);
 
    
 
    if ($matched)
 
    {
 
        if ($flags & PREG_SET_ORDER)
 
        {
 
            $ret = array();
 
            $number = count($matches[0]);
 
            
 
            foreach ($matches as $match)
 
            {
 
                for ($i=0;$i<$number;++$i)
 
                {
 
                    if (!isset($ret[$i]))
 
                    {
 
                        $ret[$i] = array();
 
                    }
 
                    
 
                    $ret[$i][] = isset($match[$i]) ? $match[$i] : null;
 
                }
 
            }
 
            
 
            $matches = $ret;
 
        }
 
    }
 
    
 
    return $matched;
 
}
 
 
?>
 
 
 |