DBA
[ class tree: DBA ] [ index: DBA ] [ all elements ]

Source for file File.php

Documentation is available at File.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | Copyright (c) 2002-2003 Brent Cook                                        |
  5. // +----------------------------------------------------------------------+
  6. // | This library is free software; you can redistribute it and/or        |
  7. // | modify it under the terms of the GNU Lesser General Public           |
  8. // | License as published by the Free Software Foundation; either         |
  9. // | version 2.1 of the License, or (at your option) any later version.   |
  10. // |                                                                      |
  11. // | This library is distributed in the hope that it will be useful,      |
  12. // | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
  13. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
  14. // | Lesser General Public License for more details.                      |
  15. // |                                                                      |
  16. // | You should have received a copy of the GNU Lesser General Public     |
  17. // | License along with this library; if not, write to the Free Software  |
  18. // | Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA|
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: File.php,v 1.12 2003/01/27 04:31:42 busterb Exp $
  22.  
  23. require_once 'DBA.php';
  24.  
  25. // {{{ constants
  26. /**
  27.  * Location in the index file for a block location
  28.  */
  29. define('DBA_SIMPLE_LOC',0);
  30.  
  31. /**
  32.  * Location in the index file for a block size
  33.  */
  34. define('DBA_SIMPLE_SIZE',1);
  35.  
  36. /**
  37.  * Location in the index file for a block value size
  38.  */
  39. define('DBA_SIMPLE_VSIZE',2);
  40.  
  41. /**
  42.  * Location in the index file for a block key
  43.  */
  44. define('DBA_SIMPLE_KEY',3);
  45. // }}}
  46.  
  47. /**
  48.  * DBA_Driver_File provides a simple, file-based implementation of a
  49.  * DBM-style database. It uses two files, and index and a data file to
  50.  * manage key/value pairs. These two files use the suffixes '.dat' and
  51.  * '.idx'. When a database is opened, only the index file is read. The
  52.  * index file contains pointers to locations within the data file, which
  53.  * are used to retrieve values.
  54.  *
  55.  * The class uses a concept of blocks for data storage. When the first value
  56.  * is inserted, a new block is created by appending to the data file. If that
  57.  * value is deleted, it remains in the data file, but is marked as empty in
  58.  * the index file. A list of available blocks is kept, so when a new value
  59.  * is inserted, its size is compared to the list of available blocks. If one
  60.  * is of sufficient size, it is reused and marked as used in the index file.
  61.  * Blocks can be of any length.
  62.  *
  63.  * In updating the index, lines are simply appended to the file after each
  64.  * operation. So, the index file might have the same block listed multiple time
  65.  * , just in different states. When the database is closed, it rewrites the
  66.  * index file, removing and duplicate entries for a single block. The index
  67.  * reader only uses the last entry for a block from the index file, so if close
  68.  * is not called for some reason, the index file is still in a valid state.
  69.  *
  70.  * The optimize function merely removes duplicated index entries by rewriting
  71.  * the file, the same as close.
  72.  * The sync function calls fflush on the data and index files.
  73.  *
  74.  * @author  Brent Cook
  75.  * @version 1.0
  76.  * @access  public
  77.  * @package DBA
  78.  */
  79. class DBA_Driver_File extends DBA
  80. {
  81.     // {{{ instance variables
  82.     /**
  83.      * Name of the database
  84.      * @access private
  85.      */
  86.     var $_dbName;
  87.  
  88.     /**
  89.      * Handle to data file
  90.      * @access private
  91.      */
  92.     var $_datFP;
  93.  
  94.     /**
  95.      * Handle to index file
  96.      * @access private
  97.      */
  98.     var $_idxFP;
  99.  
  100.     /**
  101.      * Indicates the current ability for read/write operations
  102.      * @access private
  103.      */
  104.     var $_writable;
  105.  
  106.     /**
  107.      * Indicates the current ability for read operations
  108.      * @access private
  109.      */
  110.     var $_readable;
  111.  
  112.     /**
  113.      * Determines if the driver should use an index file
  114.      * @access private
  115.      */
  116.     var $_indexed;
  117.     // }}}
  118.  
  119.     // {{{ DBA_Driver_File($indexed = true)
  120.     /* Constructor
  121.      *
  122.      * @access public
  123.      * @param   string  $driver dba driver to use
  124.      */
  125.     function DBA_Driver_File($indexed = true)
  126.     {
  127.         // call the base constructor
  128.         $this->DBA();
  129.         $this->_indexed = true;
  130.     }
  131.     // }}}
  132.  
  133.     // {{{ open($dbName='', $mode='r', $persistent = false)
  134.     /**
  135.      * Opens a database.
  136.      *
  137.      * @access  public
  138.      * @param   string  $dbName The name of a database
  139.      * @param   string  $mode The mode in which to open a database.
  140.      *                    'r' opens read-only.
  141.      *                    'w' opens read-write.
  142.      *                    'n' creates a new database and opens read-write.
  143.      *                    'c' creates a new database if the database does not
  144.      *                       exist and opens read-write.
  145.      * @param   boolean $persistent Determines whether to open the database
  146.      *                   peristently. Not supported here.
  147.      * @return  object  PEAR_Error on failure
  148.      */
  149.     function open($dbName=''$mode='r'$persistent = false)
  150.     {
  151.         if ($persistent{
  152.             return $this->raiseError(DBA_ERROR_UNSUP_PERSISTENCE);
  153.         }
  154.  
  155.         if ($dbName == ''{
  156.             return $this->raiseError(DBA_ERROR_NO_DBNAME);
  157.         else {
  158.             $this->_dbName $dbName;
  159.             $dat_name $dbName.'.dat';
  160.             $idx_name $dbName.'.idx';
  161.         }
  162.  
  163.         switch ($mode{
  164.             case 'r':
  165.                     // open for reading
  166.                     $file_mode 'rb';
  167.                     $this->_writable = false;
  168.                     $this->_readable = true;
  169.                     break;
  170.             case 'n':
  171.                     // create a new database
  172.                     $file_mode 'w+b';
  173.                     $this->_writable = true;
  174.                     $this->_readable = true;
  175.                     break;
  176.             case 'c':
  177.                     // should we create a new database?
  178.                     if (!DBA_Driver_File::db_exists($dbName)) {
  179.                         $file_mode 'w+b';
  180.                         $this->_writable = true;
  181.                         $this->_readable = true;
  182.                         break;
  183.                     // otherwise, we just open for writing
  184.             case 'w':
  185.                     $file_mode 'r+b';
  186.                     $this->_writable = true;
  187.                     $this->_readable = true;
  188.                     break;
  189.             default:
  190.                 return $this->raiseError(DBA_ERROR_INVALID_MODENULLNULL,
  191.                     'filemode: '.$mode);
  192.         }
  193.  
  194.         // open the index file
  195.         $this->_idxFP @fopen($idx_name$file_mode);
  196.         if ($this->_idxFP === false{
  197.             $this->_writable = false;
  198.             $this->_readable = false;
  199.             return $this->raiseError(DBA_ERROR_CANNOT_OPENNULLNULL,
  200.                 'could not open idx file '.$idx_name);
  201.         }
  202.  
  203.         // open the data file
  204.         $this->_datFP @fopen($dat_name$file_mode);
  205.         if ($this->_datFP === false{
  206.             fclose ($this->_idxFP);
  207.             $this->_writable = false;
  208.             $this->_readable = false;
  209.             return $this->raiseError(DBA_ERROR_CANNOT_OPENNULLNULL,
  210.                 'could not open data file '.$dat_name);
  211.         }
  212.  
  213.         // get a shared lock if read-only, otherwise get an exclusive lock
  214.         if ($file_mode == 'r'{
  215.             flock ($this -> _idxFPLOCK_SH);
  216.             flock ($this -> _datFPLOCK_SH);
  217.         else {
  218.             flock ($this -> _idxFPLOCK_EX);
  219.             flock ($this -> _datFPLOCK_EX);
  220.         }
  221.  
  222.         // we are writing to a new file, so we do not need to read anything
  223.         if ($file_mode != 'w+'{
  224.             // parse the index file
  225.             $this->_readIdx();
  226.         }
  227.     }
  228.     // }}}
  229.  
  230.     // {{{ close()
  231.     /**
  232.      * Closes an open database.
  233.      *
  234.      * @access  public
  235.      * @return  object  PEAR_Error on failure
  236.      */
  237.     function close()
  238.     {
  239.         if ($this->isOpen())
  240.         {
  241.             if ($this->isWritable())
  242.             {
  243.                 $this->_writeIdx();
  244.             }
  245.             $this->_readable = false;
  246.             $this->_writable = false;
  247.             fclose($this->_idxFP);
  248.             fclose($this->_datFP);
  249.         else {
  250.             return $this->raiseError(DBA_ERROR_NOT_OPEN);
  251.         }
  252.     }
  253.     // }}}
  254.  
  255.     // {{{ reopen($mode)
  256.     /**
  257.      * Reopens an already open database in read-only or write mode.
  258.      * If the database is already in the requested mode, then this function
  259.      * does nothing.
  260.      *
  261.      * @access  public
  262.      * @param   string  $mode 'r' for read-only, 'w' for read/write
  263.      * @return  object  PEAR_Error on failure
  264.      */
  265.     function reopen($mode)
  266.     {
  267.         if ($this->isOpen()) {
  268.             if (($mode == 'r'&& $this->isWritable()) {
  269.                 // Reopening as read-only
  270.                 $this->close();
  271.                 return $this->open($this->_dbName'r');
  272.             else {
  273.                 if (($mode == 'w'&& (!$this -> _writable))
  274.                 {
  275.                     // Reopening as read-write
  276.                     $this->close();
  277.                     return $this->open($this->_dbName'w');
  278.                 }
  279.             }
  280.         else {
  281.             return $this->raiseError(DBA_ERROR_NOT_OPEN);
  282.         }
  283.     }
  284.     // }}}
  285.  
  286.     // {{{ _DBA_Driver_File()
  287.     /**
  288.      * PEAR emulated destructor calls close on PHP shutdown
  289.      * @access private
  290.      */
  291.     function _DBA_Driver_File()
  292.     {
  293.         $this->close();
  294.     }
  295.     // }}}
  296.  
  297.     // {{{ getName()
  298.     /**
  299.      * Returns the name of the opened database. Assumes database is open
  300.      * @returns string the name of the opened database
  301.      */
  302.     function getName()
  303.     {
  304.         return $this->_dbName;
  305.     }
  306.     // }}}
  307.  
  308.     // {{{ isOpen()
  309.     /**
  310.      * Returns the current open status for the database
  311.      *
  312.      * @access  public
  313.      * @return  boolean true if the database is open, false if it is closed
  314.      */
  315.     function isOpen()
  316.     {
  317.         return($this->_readable || $this->_writable);
  318.     }
  319.     // }}}
  320.  
  321.     // {{{ isReadable()
  322.     /**
  323.      * Returns the current read status for the database
  324.      *
  325.      * @access  public
  326.      * @return  boolean true if the database is readable, false if it is not
  327.      */
  328.     function isReadable()
  329.     {
  330.         return $this->_readable;
  331.     }
  332.     // }}}
  333.  
  334.     // {{{ isWritable()
  335.     /**
  336.      * Returns the current write status for the database
  337.      *
  338.      * @access  public
  339.      * @return  boolean true if the database is writable, false if it is not
  340.      */
  341.      function isWritable()
  342.      {
  343.          return $this->_writable;
  344.      }
  345.     // }}}
  346.  
  347.     // {{{ remove($key)
  348.     /**
  349.      * Removes the value at location $key
  350.      *
  351.      * @access  public
  352.      * @param   string  $key key to remove
  353.      * @return  object  PEAR_Error on failure
  354.      */
  355.     function remove($key)
  356.     {
  357.         if ($this->isWritable()) {
  358.             if (isset($this->_usedBlocks[$key])) {
  359.                 $this->_freeUsedBlock($key);
  360.             else {
  361.                 return $this->raiseError(DBA_ERROR_NOT_FOUNDNULLNULL'key: '.$key);
  362.             }
  363.         else {
  364.             return $this->raiseError(DBA_ERROR_NOT_WRITEABLE);
  365.         }
  366.     }
  367.     // }}}
  368.  
  369.     // {{{ &fetch($key)
  370.     /**
  371.      * Returns the value that is stored at $key.
  372.      *
  373.      * @access  public
  374.      * @param   string $key key to examine
  375.      * @return  mixed  the requested value on success, false on failure
  376.      */
  377.     function &fetch($key)
  378.     {
  379.         if ($this->isReadable()) {
  380.             if (!isset($this->_usedBlocks[$key])) {
  381.                 return $this->raiseError(DBA_ERROR_NOT_FOUNDNULLNULL'key: '.$key);
  382.             else {
  383.                 fseek($this->_datFP$this->_usedBlocks[$key][DBA_SIMPLE_LOC]);
  384.                 return fread($this->_datFP,$this->_usedBlocks[$key][DBA_SIMPLE_VSIZE]);
  385.             }
  386.         else {
  387.             return $this->raiseError(DBA_ERROR_NOT_READABLE);
  388.         }
  389.     }
  390.     // }}}
  391.  
  392.     // {{{ firstkey()
  393.     /**
  394.      * Returns the first key in the database
  395.      *
  396.      * @access  public
  397.      * @return  mixed  string on success, false on failure
  398.      */
  399.     function firstkey()
  400.     {
  401.         if ($this->isReadable(&& ($this->size(> 0)) {
  402.             reset($this->_usedBlocks);
  403.             return key($this->_usedBlocks);
  404.         else {
  405.             return false;
  406.         }
  407.     }
  408.     // }}}
  409.  
  410.     // {{{ nextkey()
  411.     /**
  412.      * Returns the next key in the database, false if there is a problem
  413.      *
  414.      * @access  public
  415.      * @return  mixed  string on success, false on failure
  416.      */
  417.     function nextkey()
  418.     {
  419.         if ($this->isReadable(&& ($this->size(> 0)
  420.             && next($this->_usedBlocks)) {
  421.             return key($this->_usedBlocks);
  422.         else {
  423.             return false;
  424.         }
  425.     }
  426.     // }}}
  427.  
  428.  
  429.     // {{{ getkeys()
  430.     /**
  431.      * Returns all keys in the database
  432.      *
  433.      * @access  public
  434.      * @return  mixed  array
  435.      */
  436.     function getkeys()
  437.     {
  438.         if ($this->isReadable(&& ($this->size(> 0)) {
  439.             return array_keys($this->_usedBlocks);
  440.         else {
  441.             return array();
  442.         }
  443.     }
  444.     // }}}
  445.  
  446.     // {{{ size()
  447.     /**
  448.      * Calculates the size of the database in number of keys
  449.      *
  450.      * @access  public
  451.      * @return  int    number of keys in the database
  452.      */
  453.     function size()
  454.     {
  455.         if (is_array($this->_usedBlocks)) {
  456.             return sizeof($this->_usedBlocks);
  457.         else {
  458.             return 0;
  459.         }
  460.     }
  461.     // }}}
  462.  
  463.     // {{{ insert($key, $value)
  464.     /**
  465.      * Inserts a new value at $key. Will not overwrite if the key/value pair
  466.      * already exist
  467.      *
  468.      * @access  public
  469.      * @param   string  $key key to insert
  470.      * @param   string  $value value to store
  471.      * @return  object  PEAR_Error on failure
  472.      */
  473.     function insert($key$value)
  474.     {
  475.         if ($this->exists($key)) {
  476.             return $this->raiseError(DBA_ERROR_ALREADY_EXISTSNULLNULL,
  477.                 'key: '.$key);
  478.         else {
  479.             return $this->replace($key$value);
  480.         }
  481.     }
  482.     // }}}
  483.  
  484.     // {{{ replace($key, $value)
  485.     /**
  486.      * Inserts a new value at key. If the key/value pair
  487.      * already exist, overwrites the value
  488.      *
  489.      * @access  public
  490.      * @param   $key    string the key to insert
  491.      * @param   $val    string the value to store
  492.      * @return  object  PEAR_Error on failure
  493.      */
  494.     function replace($key$value)
  495.     {
  496.         // is the database in a usable state?
  497.         if ($this->isWritable()) {
  498.             // get how much space we need
  499.             $vsize strlen($value);
  500.  
  501.             if (!isset($this->_usedBlocks[$key])) {
  502.                 // the value is new
  503.                 $this->_writeNewBlock($key$value$vsize);
  504.  
  505.             else {
  506.                 // the value is not new
  507.                 $size $this->_usedBlocks[$key][DBA_SIMPLE_SIZE];
  508.  
  509.                 // is the value smaller or equal in size to its block size
  510.                 if ($size >= $vsize{
  511.                     // move to the block's location in the data file
  512.                     $loc $this->_usedBlocks[$key][DBA_SIMPLE_LOC];
  513.                     fseek($this->_datFP$loc);
  514.  
  515.                     // write to the data file
  516.                     fwrite($this->_datFPstr_pad($value$size)$size);
  517.  
  518.                     // update internal indecies
  519.                     $this->_usedBlocks[$key][DBA_SIMPLE_VSIZE$vsize;
  520.                     $this->_writeIdxEntry($loc$size$vsize$key);
  521.  
  522.                 // the value is larger than its allocated space
  523.                 else {
  524.                     // free this value's allocated block
  525.                     $this->_freeUsedBlock($key);
  526.  
  527.                     $this->_writeNewBlock($key$value$vsize);
  528.                 }
  529.             }
  530.         else {
  531.             return $this->raiseError(DBA_ERROR_NOT_WRITEABLE);
  532.         }
  533.     }
  534.     // }}}
  535.     
  536.     // {{{ _writeNewBlock($key, $value, $vsize)
  537.     /**
  538.      * Allocates a new block of at least $vsize and writes $key=>$val
  539.      * to the database
  540.      *
  541.      * @access  private
  542.      * @param   string $key 
  543.      * @param   string $value 
  544.      * @param   int    $vsize 
  545.      */
  546.     function _writeNewBlock($key$value$vsize)
  547.     {
  548.         // is there is a sufficiently sized block free ?
  549.         $loc $this->_getFreeBlock($vsize);
  550.         if ($loc !== false{
  551.             // update free block list
  552.             $size $this->_freeBlocks[$loc];
  553.             unset($this->_freeBlocks[$loc]);
  554.  
  555.             // move to the block's location in the data file
  556.             fseek($this->_datFP$locSEEK_SET);
  557.  
  558.             // write to the data file
  559.             fwrite($this->_datFPstr_pad($value,$size)$size);
  560.  
  561.             $this->_usedBlocks[$key= array($loc$size$vsize);
  562.             $this->_writeIdxEntry($loc$size$vsize$key);
  563.  
  564.         // there is not a sufficiently sized block free
  565.         else {
  566.             // move to the end of the data file
  567.             fseek($this ->_datFP0SEEK_END);
  568.             $loc ftell($this->_datFP);
  569.  
  570.             // write to the data file
  571.             $size $vsize ceil($vsize / 20)// make size 5% larger
  572.  
  573.             // add a useless "\n" to new values. This makes the data file
  574.             // readable in any text editor. Useful when things go wrong :P
  575.             fwrite($this->_datFPstr_pad($value$size)."\n"$size+1);
  576.  
  577.             // update internal block lists
  578.             $this->_usedBlocks[$key= array($loc$size$vsize);
  579.             $this->_writeIdxEntry($loc$size$vsize$key);
  580.         }
  581.     }
  582.     // }}}
  583.  
  584.     // {{{ _getFreeBlock($reqsize)
  585.     /**
  586.      * Returns a block location from the free list
  587.      *
  588.      * @access  private
  589.      * @param   int     $reqsize Requested size
  590.      * @returns mixed  location of free block, false if there are no free blocks
  591.      */
  592.     function _getFreeBlock($reqsize)
  593.     {
  594.         // check if we have any blocks to choose from
  595.         if (is_array($this->_freeBlocks)) {
  596.             // iterate through the blocks in blockIndex to find
  597.             // a free block
  598.             foreach ($this->_freeBlocks as $loc=>$size{
  599.                 if ($size >= $reqsize{
  600.                     return $loc;
  601.                 }
  602.             }
  603.         }
  604.         // no blocks available
  605.         return false;
  606.     }
  607.     // }}}
  608.  
  609.     // {{{ _freeUsedBlock($key)
  610.     /**
  611.      * Places a used block on the free list, updates indicies accordingly
  612.      *
  613.      * @access  private
  614.      * @param   string $key 
  615.      * @returns mixed
  616.      */
  617.     function _freeUsedBlock($key)
  618.     {
  619.         $loc $this->_usedBlocks[$key][DBA_SIMPLE_LOC];
  620.         $size $this->_usedBlocks[$key][DBA_SIMPLE_SIZE];
  621.         unset($this->_usedBlocks[$key]);
  622.  
  623.         $this->_freeBlocks[$loc$size;
  624.         $this->_writeIdxEntry($loc$size);
  625.     }
  626.     // }}}
  627.  
  628.     // {{{ create($dbName)
  629.     /**
  630.      * Creates a new database file if one does not exist. If it already exists,
  631.      * updates the last-updated timestamp on the database
  632.      *
  633.      * @access  public
  634.      * @param   string  $dbName the database to create
  635.      * @return  object  PEAR_Error on failure
  636.      */
  637.     function create($dbName)
  638.     {
  639.         if (!(@touch($dbName.'.dat'&& @touch($dbName.'.idx'))) {
  640.            return $this->raiseError('Could not create database: '.$dbName);
  641.         }
  642.     }
  643.     // }}}
  644.  
  645.     // {{{ db_exists($dbName)
  646.     /**
  647.      * Indicates whether a database with given name exists
  648.      *
  649.      * @access  public
  650.      * @param   string   $dbName the database name to check for existence
  651.      * @return  boolean true if the database exists, false if it doesn't
  652.      */
  653.     function db_exists($dbName)
  654.     {
  655.         return(file_exists($dbName.'.dat'&& file_exists($dbName.'.idx'));
  656.     }
  657.     // }}}
  658.  
  659.     // {{{ db_drop($dbName)
  660.     /**
  661.      * Removes a database from existence
  662.      *
  663.      * @access  public
  664.      * @param   string  $dbName the database name to drop
  665.      * @return  object  PEAR_Error on failure
  666.      */
  667.     function db_drop($dbName)
  668.     {
  669.         if (DBA_Driver_File::db_exists($dbName)) {
  670.             if (!unlink($dbName.'.dat'|| !unlink($dbName.'.idx')) {
  671.                 return $this->raiseError(DBA_ERROR_CANNOT_DROPNULLNULL,
  672.                     'dbname: '.$dbName);
  673.             }
  674.         else {
  675.             return $this->raiseError(DBA_ERROR_NOSUCHDBNULLNULL,
  676.                 'dbname: '.$dbName);
  677.         }
  678.     }
  679.     // }}}
  680.  
  681.     // {{{ drop($dbName)
  682.     /**
  683.      * Removes the last open database from existence
  684.      *
  685.      * @access  public
  686.      * @return  object  PEAR_Error on failure
  687.      */
  688.     function drop()
  689.     {
  690.         $this->close();
  691.         return $this->db_drop($this->_dbName);
  692.     }
  693.     // }}}
  694.  
  695.     // {{{ exists($key)
  696.     /**
  697.      * Check whether key exists
  698.      *
  699.      * @access  public
  700.      * @param   string   $key 
  701.      * @return  boolean true if the key exists, false if it doesn't
  702.      */
  703.     function exists($key)
  704.     {
  705.         return($this->isOpen(&& isset($this->_usedBlocks[$key]));
  706.     }
  707.     // }}}
  708.  
  709.     // {{{ sync()
  710.     /**
  711.      * Synchronizes an open database to disk
  712.      * @access  public
  713.      */
  714.     function sync()
  715.     {
  716.         if ($this->isWritable()) {
  717.             fflush($this->_datFP);
  718.             fflush($this->_idxFP);
  719.         }
  720.     }
  721.     // }}}
  722.  
  723.     // {{{ optimize()
  724.     /**
  725.      * Optimizes an open database
  726.      * @access  public
  727.      */
  728.     function optimize()
  729.     {
  730.         if ($this->isWritable()) {
  731.             $this->_writeIdx();
  732.         }
  733.     }
  734.     // }}}
  735.  
  736.     // {{{ _readIdx()
  737.     /**
  738.      * Reads the entries in an index file
  739.      * Assumes that $this->_idxFP is valid and readable
  740.      * @access private
  741.      */
  742.     function _readIdx()
  743.     {
  744.         // clear out old data if a previous database was opened
  745.         $this->_usedBlocks = array();
  746.         $this->_freeBlocks = array();
  747.         $usedBlocks = array()// temporary used index
  748.         $key '';            // reset key
  749.  
  750.         while (fscanf($this->_idxFP'%u|%u|%u|%s'$loc$size$vsize$key))
  751.         {
  752.             // is this an free block?
  753.             if ($key == ''{
  754.                 // check if this block had been previously marked as used
  755.                 if (isset($usedBlocks[$loc])) {
  756.                     unset($this->_usedBlocks[$usedBlocks[$loc]]);
  757.                     unset($usedBlocks[$loc]);
  758.                 }
  759.  
  760.                 $this->_freeBlocks[$loc$size;
  761.             else {
  762.                 // check if this block had been previously marked as free
  763.                 if (isset($this->_freeBlocks[$loc])) {
  764.                     unset($this->_freeBlocks[$loc]);
  765.                 }
  766.  
  767.                 $this->_usedBlocks[$key= array($loc$size$vsize);
  768.                 $usedBlocks[$loc$key;
  769.             }
  770.             $key ''// reset key for the next iteration
  771.         }
  772.     }
  773.     // }}}
  774.  
  775.     // {{{ _writeIdx()
  776.     /**
  777.      * Rewrites the index file, removing free entries
  778.      * Assumes that $this->_idxFP is valid and writable
  779.      *
  780.      * @access private
  781.      */
  782.     function _writeIdx()
  783.     {
  784.         // move the file pointer to the beginning; ftruncate does not do this
  785.         fseek($this->_idxFP0);
  786.  
  787.         // clear the index
  788.         ftruncate($this->_idxFP0);
  789.  
  790.         // write the free blocks
  791.         if (isset($this->_freeBlocks)) {
  792.             foreach ($this->_freeBlocks as $loc=>$size{
  793.                 $this->_writeIdxEntry($loc,$size);
  794.             }
  795.         }
  796.  
  797.         // write the used blocks
  798.         if (isset($this->_usedBlocks)) {
  799.             foreach ($this->_usedBlocks as $key=>$block{
  800.                 $this->_writeIdxEntry($block[DBA_SIMPLE_LOC],
  801.                                       $block[DBA_SIMPLE_SIZE],
  802.                                       $block[DBA_SIMPLE_VSIZE]$key);
  803.             }
  804.         }
  805.         fflush($this->_idxFP);
  806.     }
  807.     // }}}
  808.  
  809.     // {{{ _writeIdxEntry($loc, $size, $vsize=NULL, $key=NULL)
  810.     /**
  811.      * Writes a used block entry to an index file
  812.      *
  813.      * @access private
  814.      */
  815.     function _writeIdxEntry($loc$size$vsize=NULL$key=NULL)
  816.     {
  817.         if (is_null($vsize)) {
  818.             // write a free block entry
  819.             fputs($this->_idxFP"$loc|$size\n");
  820.         else {
  821.             // write a used block entry
  822.             fputs($this->_idxFP"$loc|$size|$vsize|$key\n");
  823.         }
  824.     }
  825.     // }}}
  826. }
  827. ?>

Documentation generated on Mon, 11 Mar 2019 14:59:54 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.