Memo   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 72
c 2
b 0
f 0
dl 0
loc 128
rs 10
wmc 23

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 2 1
A readMemo() 0 25 4
A parseDBase4() 0 5 2
A getHeaders() 0 5 2
A readHeaders() 0 30 5
A close() 0 3 2
A parseDBase3() 0 5 2
A getMemo() 0 5 2
A open() 0 5 2
A __construct() 0 4 1
1
<?php
2
/********************************************
3
 * DBF-file MEMO-fields Reader
4
 *
5
 * Author: Chizhov Nikolay <[email protected]>
6
 * (c) 2019-2024 CIOB "Inok"
7
 ********************************************/
8
9
namespace Inok\Dbf;
10
11
use Exception;
12
13
class Memo {
14
  private $headers = null;
15
16
  private $db, $fp;
17
  private $signature = [
18
    0 => "template|picture",
19
    1 => "text",
20
    2 => "object",
21
    4294903808 => "dbaseIV"
22
  ];
23
  private $isBase4 = false;
24
  private $isBase3 = false;
25
26
  /**
27
   * @throws Exception
28
   */
29
  public function __construct($file) {
30
    $this->db = $file;
31
32
    $this->open();
33
  }
34
35
  public function __destruct() {
36
    $this->close();
37
  }
38
39
  /**
40
   * @throws Exception
41
   */
42
  private function open() {
43
    if (!file_exists($this->db)) {
44
      throw new Exception(sprintf('Memo-file %s cannot be found', $this->db));
45
    }
46
    $this->fp = fopen($this->db, "rb");
47
  }
48
49
  public function getHeaders() {
50
    if (is_null($this->headers)) {
51
      $this->readHeaders();
52
    }
53
    return $this->headers;
54
  }
55
56
  public function getMemo($block) {
57
    if (is_null($this->headers)) {
58
      $this->readHeaders();
59
    }
60
    return $this->readMemo($block);
61
  }
62
63
  public function close() {
64
    if (get_resource_type($this->fp) === "file") {
65
      fclose($this->fp);
66
    }
67
  }
68
69
  private function readHeaders() {
70
    $data = fread($this->fp, 512);
71
    $fileExt = strtolower(pathinfo($this->db, PATHINFO_EXTENSION));
0 ignored issues
show
Bug introduced by
It seems like pathinfo($this->db, Inok\Dbf\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
    $fileExt = strtolower(/** @scrutinizer ignore-type */ pathinfo($this->db, PATHINFO_EXTENSION));
Loading history...
72
    $fileName = "";
73
    if ($fileExt == "dbt") {
74
      $fileName = trim(substr($data, 8, 8));
75
      if (empty($fileName)) {
76
        $this->isBase3 = true;
77
      } else {
78
        $this->isBase4 = true;
79
      }
80
    }
81
    if ($this->isBase3) {
82
      $this->headers = [
83
        "freeblock_position" => unpack("L", substr($data, 0, 4))[1],
84
        "block_size" => 512
85
      ];
86
      return;
87
    }
88
    if ($this->isBase4) {
89
      $this->headers = [
90
        "freeblock_position" => unpack("L", substr($data, 0, 4))[1],
91
        "block_size" => unpack("S", substr($data, 20, 2))[1],
92
        "dbf-file" => $fileName
93
      ];
94
      return;
95
    }
96
    $this->headers = [
97
      "freeblock_position" => unpack("N", substr($data, 0, 4))[1],
98
      "block_size" => unpack("n", substr($data, 6, 2))[1]
99
    ];
100
  }
101
102
  private function readMemo($block) {
103
    fseek($this->fp, $this->headers["block_size"] * $block);
104
    if ($this->isBase3) {
105
      $text = "";
106
      while (!preg_match('/\x1a\x1a/', $text)) {
107
        $text .= fread($this->fp, 512);
108
      }
109
      $memo["text"] = $this->parseDBase3($text);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$memo was never initialized. Although not strictly required by PHP, it is generally a good practice to add $memo = array(); before regardless.
Loading history...
110
      return $memo;
111
    }
112
    $data = fread($this->fp, 8);
113
    if ($this->isBase4) {
114
      $memo = [
115
        "signature" => $this->signature[unpack("N", substr($data, 0, 4))[1]],
116
        "length" => octdec(intval(bin2hex(trim(substr($data, 4, 4)))))
117
      ];
118
      $memo["text"] = $this->parseDBase4(fread($this->fp, $memo["length"]));
0 ignored issues
show
Bug introduced by
It seems like $memo['length'] can also be of type double; however, parameter $length of fread() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
      $memo["text"] = $this->parseDBase4(fread($this->fp, /** @scrutinizer ignore-type */ $memo["length"]));
Loading history...
119
      return $memo;
120
    }
121
    $memo = [
122
      "signature" => $this->signature[unpack("N", substr($data, 0, 4))[1]],
123
      "length" => unpack("N", substr($data, 4, 4))[1]
124
    ];
125
    $memo["text"] = fread($this->fp, $memo["length"]);
126
    return $memo;
127
  }
128
129
  private function parseDBase3($text) {
130
    if (preg_match('/\x1a\x1a/', $text, $matches, PREG_OFFSET_CAPTURE)) {
131
      $text = substr($text, 0, $matches[0][1]);
132
    }
133
    return $text;
134
  }
135
136
  private function parseDBase4($text) {
137
    if (preg_match('/\x0d\x0a/', $text, $matches, PREG_OFFSET_CAPTURE)) {
138
      $text = substr($text, 0, $matches[0][1]);
139
    }
140
    return preg_replace('/\x8d\x0a/', "\n", $text);
141
  }
142
}
143