Skip to content

Commit

Permalink
MemoFactory for different Memo types;
Browse files Browse the repository at this point in the history
VisualFoxproTest;
add FieldTypes [GENERAL, BLOB, CURRENCY, VAR_FIELD, VARBINARY]
  • Loading branch information
gam6itko committed Feb 21, 2020
1 parent 440381f commit 193f6b3
Show file tree
Hide file tree
Showing 31 changed files with 971 additions and 123 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
"dev-master": "1.2.x-dev"
}
}
}
44 changes: 28 additions & 16 deletions src/XBase/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,41 @@ class Column
/** @var int */
public $decimalCount;

/**
* @var int Field address within record.
*/
protected $memAddress;
protected $workAreaID;
protected $setFields;
protected $indexed;
/**
* @var int|null
* @deprecated same as memAddress
*/
protected $bytePos;
protected $colIndex;

/**
* Column constructor.
*
* @param string $name
* @param string $type
* @param $memAddress
* @param $length
* @param $decimalCount
* @param $reserved1
* @param $workAreaID
* @param $reserved2
* @param $setFields
* @param $reserved3
* @param $indexed
* @param $colIndex
* @param $bytePos
* @param string $name
* @param string $type
* @param int $memAddress
* @param int $length
* @param int $decimalCount
* @param string $reserved1
* @param int $workAreaID
* @param string $reserved2
* @param bool $setFields
* @param string $reserved3
* @param int $indexed
* @param int $colIndex
* @param int|null $bytePos
*/
public function __construct($name, $type, $memAddress, $length, $decimalCount, $reserved1, $workAreaID, $reserved2, $setFields, $reserved3, $indexed, $colIndex, $bytePos)
public function __construct($name, $type, $memAddress, $length, $decimalCount, $reserved1, $workAreaID, $reserved2, $setFields, $reserved3, $indexed, $colIndex, $bytePos = null)
{
$this->rawname = $name;
// first byte is 'deleted mark'
$this->name = (strpos($name, chr(0x00)) !== false) ? substr($name, 0, strpos($name, chr(0x00))) : $name;
$this->type = $type;
$this->memAddress = $memAddress;
Expand All @@ -50,8 +58,8 @@ public function __construct($name, $type, $memAddress, $length, $decimalCount, $
$this->workAreaID = $workAreaID;
$this->setFields = $setFields;
$this->indexed = $indexed;
$this->bytePos = $bytePos;
$this->colIndex = $colIndex;
$this->bytePos = $bytePos;
}

/**
Expand Down Expand Up @@ -97,7 +105,7 @@ public function getDataLength()
}

/**
* @return mixed
* @return int
*/
public function getMemAddress()
{
Expand Down Expand Up @@ -135,6 +143,10 @@ public function toString()
return $this->name;
}

/**
* @return int
* @deprecated use getMemAddress
*/
public function getBytePos()
{
return $this->bytePos;
Expand Down
15 changes: 11 additions & 4 deletions src/XBase/Enum/FieldType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@ final class FieldType
/** @var string Numeric */
const NUMERIC = 'N';
/** @var string Floating point */
const FLOATING = 'F';
const FLOAT = 'F';
/** @var string Date */
const DATE = 'D';
/** @var string Logical - ? Y y N n T t F f (? when not initialized). */
const LOGICAL = 'L';
/** @var string DateTime */
const DATETIME = 'T';
/** @var string Index */
const INDEX = 'I';
/** @var string Integer */
const INTEGER = 'I';
/** @var string Ignore this field */
const DBFFIELD_IGNORE_0 = '0';
const IGNORE = '0';
/** @var string (dBASE V: like Memo) OLE Objects in MS Windows versions */
const GENERAL = 'G';

const BLOB = 'W';
const CURRENCY = 'Y';
const VAR_FIELD = 'V';
const VARBINARY = 'Q';
}
5 changes: 4 additions & 1 deletion src/XBase/Enum/TableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static function isFoxpro(int $version): bool
self::VISUAL_FOXPRO,
self::VISUAL_FOXPRO_AI,
self::VISUAL_FOXPRO_VAR,
self::DBASE_III_PLUS_MEMO,
// self::DBASE_III_PLUS_MEMO,
self::DBASE_IV_SQL_TABLE_MEMO,
self::FOXPRO_MEMO,
self::FOXBASE,
Expand All @@ -70,6 +70,9 @@ public static function hasMemo(int $version): bool
self::DBASE_IV_SQL_TABLE_MEMO,
self::DBASE_IV_SQL_SYSTEM_MEMO,
self::DBASE_7_MEMO,
self::VISUAL_FOXPRO,
self::VISUAL_FOXPRO_AI,
self::VISUAL_FOXPRO_VAR,
self::FOXPRO_MEMO,
]);
}
Expand Down
50 changes: 11 additions & 39 deletions src/XBase/Memo.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,21 @@

namespace XBase;

class Memo
{
/** @var resource */
protected $fp;
/** @var Table */
protected $table;
/** @var string */
protected $tableName;
use XBase\Memo\AbstractMemo;

/**
* Memo constructor.
*
* @param Table $table
* @param string $tableName
*/
public function __construct(Table $table, $tableName)
{
$this->table = $table;
$this->tableName = $tableName;
$this->open();
}

/**
* @return bool
*/
protected function open()
class Memo extends AbstractMemo
{
public function get($data)
{
$fileName = str_replace(["dbf", "DBF"], ["fpt", "FPT"], $this->tableName);

if (!file_exists($fileName)) {
return false;
if ($data && 2 === strlen($data)) {
$pointer = unpack('s', $data)[1];
return $this->getData($pointer);
} else {
return $data;
}

$this->fp = fopen($fileName, 'rb');

return $this->fp != false;
}

/**
* @param int $pointer
*
* @return false|string|null
*/
public function get($pointer)
protected function getData($pointer)
{
$value = null;
if ($this->fp && $pointer != 0) {
Expand All @@ -69,4 +40,5 @@ public function get($pointer)
}
return $value;
}

}
83 changes: 83 additions & 0 deletions src/XBase/Memo/AbstractMemo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace XBase\Memo;

abstract class AbstractMemo implements MemoInterface
{
/** @var resource */
protected $fp;
/** @var string */
protected $filepath;
/** @var string */
protected $convertFrom;

/**
* Memo constructor.
*
* @param string $filepath
* @param string $convertFrom
*/
public function __construct($filepath, $convertFrom = null)
{
$this->filepath = $filepath;
$this->convertFrom = $convertFrom; //todo autodetect from languageCode
$this->open();
$this->readHeader();
}

public function __destruct()
{
$this->close();
}

protected function readHeader()
{

}

public static function getExtension()
{
return 'dbt';
}

/**
* @inheritDoc
*/
public function isOpen()
{
return null !== $this->fp;
}

public function open()
{
$this->close();
$this->fp = fopen($this->filepath, 'rb');
}

public function close()
{
if (null !== $this->fp) {
fclose($this->fp);
}
$this->fp = null;
}

protected function guessDataType(string $result)
{
if (strlen($result) > 4) {
$bytes = unpack('n*', substr($result, 0, 4));
switch ($bytes[1]) {
case 0x4D42: //BMP
case 0xFFD8: //JPEG
return MemoObject::TYPE_IMAGE;
case 0x8950: //PNG
if (0x4E47 === $bytes[2]) {
return MemoObject::TYPE_IMAGE;
}
break;
}
}

return MemoObject::TYPE_TEXT;
}
}
45 changes: 45 additions & 0 deletions src/XBase/Memo/DBase3Memo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace XBase\Memo;

class DBase3Memo extends AbstractMemo
{
const BLOCK_LENGTH = 512;

public static function getExtension()
{
return 'dbt';
}

public function get($pointer)
{
if (!$this->isOpen()) {
$this->open();
}

fseek($this->fp, $pointer * self::BLOCK_LENGTH);

$endMarker = chr(0x1A).chr(0x1A).chr(0x00);
$result = '';
while (!feof($this->fp)) {
$result .= fread($this->fp, 1);

$substr = substr($result, -3);
if ($endMarker === $substr) {
$result = substr($result, 0, -3);
break;
}
}

$type = $this->guessDataType($result);
if (MemoObject::TYPE_TEXT === $type && chr(0x00) === substr($result, -1)) {
$result = substr($result, 0, -1); // remove endline symbol (0x00)
}

if (MemoObject::TYPE_TEXT === $type && $this->convertFrom) {
$result = iconv($this->convertFrom, 'utf-8', $result);
}

return new MemoObject($type, $result);
}
}
49 changes: 49 additions & 0 deletions src/XBase/Memo/DBase4Memo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace XBase\Memo;

class DBase4Memo extends DBase3Memo
{
const BLOCK_SIGN = 0xFFFF0800;
const BLOCK_SIGN_LENGTH = 4;
const BLOCK_LENGTH_LENGTH = 4;

/** @var int */
protected $blockSize;
/** @var int */
protected $blockLength;

protected function readHeader()
{
fseek($this->fp, 4);
$bytes = unpack("N", fread($this->fp, 4));
$this->blockSize = $bytes[1];

fseek($this->fp, 20);
$bytes = unpack("S", fread($this->fp, 2));
$this->blockLength = $bytes[1];
}

public function get($pointer)
{
if (!$this->isOpen()) {
$this->open();
}

fseek($this->fp, $pointer * $this->blockLength);
$sign = unpack("N", fread($this->fp, self::BLOCK_SIGN_LENGTH));
if ($sign[1] !== self::BLOCK_SIGN) {
throw new \LogicException('Wrong dBaseIV block sign/');
}

$memoLength = unpack("L", fread($this->fp, self::BLOCK_LENGTH_LENGTH));
$result = fread($this->fp, $memoLength[1] - self::BLOCK_SIGN_LENGTH - self::BLOCK_LENGTH_LENGTH);

$type = $this->guessDataType($result);
if (MemoObject::TYPE_TEXT === $type && $this->convertFrom) {
$result = iconv($this->convertFrom, 'utf-8', $result);
}

return new MemoObject($type, $result);
}
}
Loading

0 comments on commit 193f6b3

Please sign in to comment.