Why my file is always empty when trying to read it using PHP?

1k views Asked by At

I am trying to write a class that will allow me to write data to a file and then read it. In some cases, I also lock the file for write, then unlock it once the writing is done.

The problem that I am having is when trying to get the content of the file using my getCache() method the file becomes empty. It seems that when the getCache() method is called the content of the file are deleted for some reason.

Here is my class

<?php

namespace ICWS;

use \ICWS\showVar;
use \DirectoryIterator;
/**
 * CacheHandler
 *
 * @package ICWS
 */
class CacheHandler {

    /* Max time second allowed to keep a session File */
    private $maxTimeAllowed = 300;


    private $filename = '';
    private $handle;

    public function __construct( $filename ){

        $this->filename = $filename;

        // 5% chance to collect garbage when the class is initialized.
        if(rand(1, 100) <= 5){
            $this->collectGarbage();
        }
    }


    /**
    * Add/Update the cached array in the session
    *
    * @param string $key
    * @param bigint $id
    * @param array $field
    * @return void
    */  
    public function updateCache($key, $id, array $field)
    {

        $currentVal = (object) $field;
        $this->openFile();

        $this->lockFile();
        $storage = $this->readFile();
        //create a new if the $id does not exists in the cache
        if( isset($storage[$key]) && array_key_exists($id, $storage[$key]) ){

            $currentVal = (object) $storage[$key][$id];

            foreach($field as $k => $v){
                $currentVal->$k = $v; //update existing key or add a new one
            }

        }

        $storage[$key][$id] =  $currentVal; 
        new showVar($storage);
        $this->updateFile($storage);
        $this->unlockFile();

        $this->closeFile();
    }

    /**
    * gets the current cache/session for a giving $key
    *
    * @param string $key
    * @return object or boolean
    */  
    public function getCache($key)
    {
        $value = false;
        $this->openFile();

        $storage = $this->readFile();

        if(isset($storage[$key])){
            $value = $storage[$key];
        }

        $this->closeFile();

        return $value;
    }

    /**
    * removes the $id from the cache/session
    *
    * @param string $key
    * @param bigint $id
    * @return boolean
    */      
    public function removeCache($key, $id)
    {

        $this->openFile();

        $this->lockFile();
        $storage = $this->readFile();

        if( !isset($storage[$key][$id])){
            $this->unlockFile();
            $this->closeFile();
            return false;
        }

        unset($storage[$key][$id]);
        $this->updateFile($storage);
        $this->unlockFile();
        $this->closeFile();
        return true;
    }



    /**
    * unset a session
    *
    * @param argument $keys
    * @return void
    */  
    public function truncateCache()
    {
        if(file_exists($this->filename)){
            unlink($this->filename);
        }
    }

    /**
    * Open a file in a giving mode
    *
    * @params string $mode 
    * @return void
    */
    private function openFile( $mode = "w+"){
        $this->handle = fopen($this->filename, $mode);

        if(!$this->handle){
            throw new exception('The File could not be opened!');
        }
    }

    /**
    * Close the file
    * @return void
    */
    private function closeFile(){

        fclose($this->handle);
        $this->handle = null;
    }

    /**
    * Update the file with array data
    * @param array $data the array in that has the data
    * @return void
    */
    private function updateFile(array $data = array() ){
        $raw = serialize($data);    
        fwrite($this->handle, $raw);
    }

    /**
    * Read the data from the opned file
    *
    * @params string $mode 
    * @return array of the data found in the file
    */
    private function readFile() {

        $length = filesize($this->filename);
        if($length > 0){
             rewind($this->handle);
            $raw = fread($this->handle, filesize($this->filename));
            return unserialize($raw);
        }

        return array();
    }


    /**
    * Lock the file
    *
    * @return void
    */  
    private function lockFile( $maxAttempts = 100, $lockType = LOCK_EX) {

        $i = 0;
        while($i <= $maxAttempts){

            // acquire an exclusive lock
            if( flock($this->handle, LOCK_EX) ){
                break;
            }

            ++$i;
        }
    }


    /**
    * Unlock the file
    *
    * @return void
    */  
    private function unlockFile() {

        fflush($this->handle);
        flock($this->handle, LOCK_UN);
    }

    /**
    * Remove add cache files
    *
    * @return void
    */  
    private function collectGarbage(){
        $mydir = dirname($this->filename);

        $dir = new DirectoryIterator( $mydir );
        $time = strtotime("now");
        foreach ($dir as $file) {

            if (  !$file->isDot()
                && $file->isFile()
                && ($time - $file->getATime()) > $this->maxTimeAllowed
                && isset($file->getFilename) && $file->getFilename != 'index.html'
            ) {
                unlink($file->getPathName());

            }

        }
    }   

}

This is how I call my class

<?php
    require 'autoloader.php';

    try {
        $cache = new \ICWS\CacheHandler('cache/12300000');

        $field = array('name' => 'Mike A', 'Address' => '123 S Main', 'phone' => '2152456245', 'ext' => 123);
        $cache->updateCache('NewKey', '123456', $field);

        echo '<pre>';
        print_r($cache->getCache('NewKey'));
        echo '</pre>';
    } catch (exception $e){

        echo $e->getMessage();
    }

?>

When commenting the line print_r($cache->getCache('NewKey')); The file 12300000 will have the data below as expected

a:1:{s:6:"NewKey";a:1:{i:123456;O:8:"stdClass":4:{s:4:"name";s:6:"Mike A";s:7:"Address";s:10:"123 S Main";s:5:"phone";s:10:"2152456245";s:3:"ext";i:123;}}}

However, when the method print_r($cache->getCache('NewKey')); is called the file becomes empty.

What am I doing wrong here? Why is my print_r($cache->getCache('NewKey')); method is emptying out the file?

1

There are 1 answers

1
wallyk On BEST ANSWER
openFile($mode = "w+")

The documentation for fopen() says:

'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.

So it resets the file, deleting everything in it.

Probably you want mode a+:

'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does not exist, attempt to create it. In this mode, fseek()() only affects the reading position, writes are always appended.