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
![]() |
|||||
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
|
|||||
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
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
![]() |
|||||
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 |