fd = $fd; // Byte 4-7 (32-bit number): Number of records in the database file. Currently 0 fseek($this->fd, 4, SEEK_SET); $this->recordCount = self::getInt32($fd); // Byte 8-9 (16-bit number): Number of bytes in the header. fseek($this->fd, 8, SEEK_SET); $this->headerLength = self::getInt16($fd); // Number of fields is (headerLength - 33) / 32) $this->fieldCount = ($this->headerLength - 33) / 32; // Byte 10-11 (16-bit number): Number of bytes in record. fseek($this->fd, 10, SEEK_SET); $this->recordLength = self::getInt16($fd); // Byte 32 - n (32 bytes each): Field descriptor array fseek($fd, 32, SEEK_SET); for ($i = 0; $i < $this->fieldCount; $i++) { $data = fread($this->fd, 32); $field = array_map('trim', unpack('a11name/a1type/c4/c1length/c1precision/s1workid/c1example/c10/c1production', $data)); $this->fields[] = $field; } } /** * dbase_close * @return void */ public function close() { fclose($this->fd); } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_get_header_info * @return array */ public function get_header_info() { // phpcs:disable return $this->fields; } /** * dbase_numfields * @return int */ public function numfields() { return $this->fieldCount; } /** * dbase_numrecords * @return int */ public function numrecords() { return $this->recordCount; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_add_record * @param array $record record * @return bool */ public function add_record($record) { // phpcs:enable if (count($record) != $this->fieldCount) { return false; } // Seek to end of file, minus the end of file marker fseek($this->fd, 0, SEEK_END); // Put the deleted flag self::putChar8($this->fd, 0x20); // Put the record if (!$this->putRecord($record)) { return false; } // Update the record count fseek($this->fd, 4); self::putInt32($this->fd, ++$this->recordCount); return true; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_replace_record * @param array $record record * @param int $record_number record number * @return bool */ public function replace_record($record, $record_number) { // phpcs:enable if (count($record) != $this->fieldCount) { return false; } if ($record_number < 1 || $record_number > $this->recordCount) { return false; } // Skip to the record location, plus the 1 byte for the deleted flag fseek($this->fd, $this->headerLength + ($this->recordLength * ($record_number - 1)) + 1); return $this->putRecord($record); } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_delete_record * @param int $record_number record number * @return bool */ public function delete_record($record_number) { // phpcs:enable if ($record_number < 1 || $record_number > $this->recordCount) { return false; } fseek($this->fd, $this->headerLength + ($this->recordLength * ($record_number - 1))); self::putChar8($this->fd, 0x2A); return true; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_get_record * @param int $record_number record number * @return array */ public function get_record($record_number) { // phpcs:enable if ($record_number < 1 || $record_number > $this->recordCount) { return false; } fseek($this->fd, $this->headerLength + ($this->recordLength * ($record_number - 1))); $record = array( 'deleted' => self::getChar8($this->fd) == 0x2A ? 1 : 0 ); foreach ($this->fields as $i => &$field) { $value = trim(fread($this->fd, $field['length'])); if ($field['type'] == 'L') { $value = strtolower($value); if ($value == 't' || $value == 'y') { $value = true; } elseif ($value == 'f' || $value == 'n') { $value = false; } else { $value = null; } } $record[$i] = $value; } return $record; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * dbase_get_record_with_names * @param int $record_number record number * @return array */ public function get_record_with_names($record_number) { // phpcs:enable if ($record_number < 1 || $record_number > $this->recordCount) { return false; } $record = $this->get_record($record_number); foreach ($this->fields as $i => &$field) { $record[$field['name']] = $record[$i]; unset($record[$i]); } return $record; } /** * dbase_pack * @return void */ public function pack() { $in_offset = $out_offset = $this->headerLength; $new_count = 0; $rec_count = $this->recordCount; while ($rec_count > 0) { fseek($this->fd, $in_offset, SEEK_SET); $record = fread($this->fd, $this->recordLength); $deleted = substr($record, 0, 1); if ($deleted != '*') { fseek($this->fd, $out_offset, SEEK_SET); fwrite($this->fd, $record); $out_offset += $this->recordLength; $new_count++; } $in_offset += $this->recordLength; $rec_count--; } ftruncate($this->fd, $out_offset); // Update the record count fseek($this->fd, 4); self::putInt32($this->fd, $new_count); } /* * A few utilitiy functions */ /** * @param string $field field * @return int */ private static function length($field) { switch ($field[1]) { case 'D': // Date: Numbers and a character to separate month, day, and year (stored internally as 8 digits in YYYYMMDD format) return 8; case 'T': // DateTime (YYYYMMDDhhmmss.uuu) (FoxPro) return 18; case 'M': // Memo (ignored): All ASCII characters (stored internally as 10 digits representing a .dbt block number, right justified, padded with whitespaces) case 'N': // Number: -.0123456789 (right justified, padded with whitespaces) case 'F': // Float: -.0123456789 (right justified, padded with whitespaces) case 'C': // String: All ASCII characters (padded with whitespaces up to the field's length) return $field[2]; case 'L': // Boolean: YyNnTtFf? (? when not initialized) return 1; } return 0; } /* * Functions for reading and writing bytes */ /** * getChar8 * @param mixed $fd file descriptor * @return int */ private static function getChar8($fd) { return ord(fread($fd, 1)); } /** * putChar8 * @param mixed $fd file descriptor * @param mixed $value value * @return bool */ private static function putChar8($fd, $value) { return fwrite($fd, chr($value)); } /** * getInt16 * @param mixed $fd file descriptor * @param int $n n * @return bool */ private static function getInt16($fd, $n = 1) { $data = fread($fd, 2 * $n); $i = unpack("S$n", $data); if ($n == 1) { return (int) $i[1]; } else { return array_merge($i); } } /** * putInt16 * @param mixed $fd file descriptor * @param mixed $value value * @return bool */ private static function putInt16($fd, $value) { return fwrite($fd, pack('S', $value)); } /** * getInt32 * @param mixed $fd file descriptor * @param int $n n * @return bool */ private static function getInt32($fd, $n = 1) { $data = fread($fd, 4 * $n); $i = unpack("L$n", $data); if ($n == 1) { return (int) $i[1]; } else { return array_merge($i); } } /** * putint32 * @param mixed $fd file descriptor * @param mixed $value value * @return bool */ private static function putInt32($fd, $value) { return fwrite($fd, pack('L', $value)); } /** * putString * @param mixed $fd file descriptor * @param mixed $value value * @param int $length length * @return bool */ private static function putString($fd, $value, $length = 254) { $ret = fwrite($fd, pack('A' . $length, $value)); } /** * putRecord * @param mixed $record record * @return bool */ private function putRecord($record) { foreach ($this->fields as $i => &$field) { $value = $record[$i]; // Number types are right aligned with spaces if ($field['type'] == 'N' || $field['type'] == 'F' && strlen($value) < $field['length']) { $value = str_repeat(' ', $field['length'] - strlen($value)) . $value; } self::putString($this->fd, $value, $field['length']); } return true; } } if (!function_exists('dbase_open')) { /** * dbase_open * @param string $filename filename * @param int $mode mode * @return DBase */ function dbase_open($filename, $mode) { return DBase::open($filename, $mode); } /** * dbase_create * @param string $filename filename * @param array $fields fields * @param int $type type * @return DBase */ function dbase_create($filename, $fields, $type = DBASE_TYPE_DBASE) { return DBase::create($filename, $fields, $type); } /** * dbase_close * @param Resource $dbase_identifier dbase identifier * @return bool */ function dbase_close($dbase_identifier) { return $dbase_identifier->close(); } /** * dbase_get_header_info * @param Resource $dbase_identifier dbase identifier * @return string */ function dbase_get_header_info($dbase_identifier) { return $dbase_identifier->get_header_info(); } /** * dbase_numfields * @param Resource $dbase_identifier dbase identifier * @return int */ function dbase_numfields($dbase_identifier) { $dbase_identifier->numfields(); } /** * dbase_numrecords * @param Resource $dbase_identifier dbase identifier * @return int */ function dbase_numrecords($dbase_identifier) { return $dbase_identifier->numrecords(); } /** * dbase_add_record * @param Resource $dbase_identifier dbase identifier * @param array $record record * @return bool */ function dbase_add_record($dbase_identifier, $record) { return $dbase_identifier->add_record($record); } /** * dbase_delete_record * @param Resource $dbase_identifier dbase identifier * @param int $record_number record number * @return bool */ function dbase_delete_record($dbase_identifier, $record_number) { return $dbase_identifier->delete_record($record_number); } /** * dbase_replace_record * @param Resource $dbase_identifier dbase identifier * @param array $record record * @param int $record_number record number * @return bool */ function dbase_replace_record($dbase_identifier, $record, $record_number) { return $dbase_identifier->replace_record($record, $record_number); } /** * dbase_get_record * @param Resource $dbase_identifier dbase identifier * @param int $record_number record number * @return bool */ function dbase_get_record($dbase_identifier, $record_number) { return $dbase_identifier->get_record($record_number); } /** * dbase_get_record_with_names * @param Resource $dbase_identifier dbase identifier * @param int $record_number record number * @return bool */ function dbase_get_record_with_names($dbase_identifier, $record_number) { return $dbase_identifier->get_record_with_names($record_number); } /** * dbase_pack * @param Resource $dbase_identifier dbase identifier * @return bool */ function dbase_pack($dbase_identifier) { return $dbase_identifier->pack(); } }