Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 37 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
MarcInJson | |
0.00% |
0 / 37 |
|
0.00% |
0 / 10 |
420 | |
0.00% |
0 / 1 |
canParse | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canParseCollection | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canParseCollectionFile | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
collectionFromString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
fromString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
toString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openCollectionFile | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
rewind | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getNextRecord | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
jsonEncode | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | |
3 | /** |
4 | * MARC-in-JSON format support class. |
5 | * |
6 | * PHP version 7 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2022. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package MARC |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki |
28 | */ |
29 | |
30 | namespace VuFind\Marc\Serialization; |
31 | |
32 | use pcrov\JsonReader\JsonReader; |
33 | |
34 | use function is_array; |
35 | use function strlen; |
36 | |
37 | /** |
38 | * MARC-in-JSON format support class. |
39 | * |
40 | * @category VuFind |
41 | * @package MARC |
42 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
43 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
44 | * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki |
45 | */ |
46 | class MarcInJson extends AbstractSerializationFile implements SerializationInterface |
47 | { |
48 | /** |
49 | * Current file |
50 | * |
51 | * @var string |
52 | */ |
53 | protected $fileName = ''; |
54 | |
55 | /** |
56 | * JSON Reader for current file |
57 | * |
58 | * @var JsonReader |
59 | */ |
60 | protected $reader = null; |
61 | |
62 | /** |
63 | * Check if this class can parse the given MARC string |
64 | * |
65 | * @param string $marc MARC |
66 | * |
67 | * @return bool |
68 | */ |
69 | public static function canParse(string $marc): bool |
70 | { |
71 | // A pretty naïve check, but it's enough to tell the different formats apart |
72 | return substr(trim($marc), 0, 1) === '{'; |
73 | } |
74 | |
75 | /** |
76 | * Check if the serialization class can parse the given MARC collection string |
77 | * |
78 | * @param string $marc MARC |
79 | * |
80 | * @return bool |
81 | */ |
82 | public static function canParseCollection(string $marc): bool |
83 | { |
84 | // A pretty naïve check, but it's enough to tell the different formats apart |
85 | return substr(trim($marc), 0, 1) === '['; |
86 | } |
87 | |
88 | /** |
89 | * Check if the serialization class can parse the given MARC collection file |
90 | * |
91 | * @param string $file File name |
92 | * |
93 | * @return bool |
94 | */ |
95 | public static function canParseCollectionFile(string $file): bool |
96 | { |
97 | if (false === ($f = fopen($file, 'rb'))) { |
98 | throw new \Exception("Cannot open file '$file' for reading"); |
99 | } |
100 | $s = ''; |
101 | do { |
102 | $s .= fgets($f, 10); |
103 | } while (strlen(ltrim($s)) < 5 && !feof($f)); |
104 | fclose($f); |
105 | |
106 | return self::canParseCollection($s); |
107 | } |
108 | |
109 | /** |
110 | * Parse MARC collection from a string into an array |
111 | * |
112 | * @param string $collection MARC record collection in the format supported by |
113 | * the serialization class |
114 | * |
115 | * @throws \Exception |
116 | * @return array |
117 | */ |
118 | public static function collectionFromString(string $collection): array |
119 | { |
120 | return json_decode($collection, true); |
121 | } |
122 | |
123 | /** |
124 | * Parse MARC-in-JSON |
125 | * |
126 | * @param string $marc JSON |
127 | * |
128 | * @throws \Exception |
129 | * @return array |
130 | */ |
131 | public static function fromString(string $marc): array |
132 | { |
133 | return json_decode($marc, true); |
134 | } |
135 | |
136 | /** |
137 | * Convert record to ISO2709 string |
138 | * |
139 | * @param array $record Record data |
140 | * |
141 | * @return string |
142 | */ |
143 | public static function toString(array $record): string |
144 | { |
145 | return self::jsonEncode($record); |
146 | } |
147 | |
148 | /** |
149 | * Open a collection file |
150 | * |
151 | * @param string $file File name |
152 | * |
153 | * @return void |
154 | * |
155 | * @throws \Exception |
156 | */ |
157 | public function openCollectionFile(string $file): void |
158 | { |
159 | $this->fileName = $file; |
160 | $this->reader = new JsonReader(); |
161 | $this->reader->open($file); |
162 | // Move into the record array: |
163 | $this->reader->read(); |
164 | } |
165 | |
166 | /** |
167 | * Rewind the collection file |
168 | * |
169 | * @return void |
170 | * |
171 | * @throws \Exception |
172 | */ |
173 | public function rewind(): void |
174 | { |
175 | if ('' === $this->fileName) { |
176 | throw new \Exception('Collection file not open'); |
177 | } |
178 | $this->openCollectionFile($this->fileName); |
179 | } |
180 | |
181 | /** |
182 | * Get next record from the file or an empty string on EOF |
183 | * |
184 | * @return string |
185 | * |
186 | * @throws \Exception |
187 | */ |
188 | public function getNextRecord(): string |
189 | { |
190 | if (null === $this->reader) { |
191 | throw new \Exception('Collection file not open'); |
192 | } |
193 | // We have to rely on the depth since the elements are anonymous: |
194 | if ($this->reader->depth() === 0) { |
195 | // Level 0 is the array enclosing the record objects, read into it: |
196 | $this->reader->read(); |
197 | } else { |
198 | // Level 1 is an object, get the next one: |
199 | $this->reader->next(); |
200 | } |
201 | $value = $this->reader->value(); |
202 | return $value ? self::jsonEncode($value) : ''; |
203 | } |
204 | |
205 | /** |
206 | * Convert a record array to a JSON string |
207 | * |
208 | * @param array $record Record |
209 | * |
210 | * @return string |
211 | */ |
212 | protected static function jsonEncode(array $record): string |
213 | { |
214 | // We need to cast any subfield with '0' as key to an object; otherwise it |
215 | // would be encoded as a simple array instead of an object: |
216 | foreach ($record['fields'] as &$fieldData) { |
217 | $field = current($fieldData); |
218 | if (!is_array($field)) { |
219 | continue; |
220 | } |
221 | foreach ($field['subfields'] as &$subfield) { |
222 | if (key($subfield) == 0) { |
223 | $subfield = (object)$subfield; |
224 | } |
225 | } |
226 | unset($subfield); |
227 | $fieldData = [key($fieldData) => $field]; |
228 | } |
229 | unset($fieldData); |
230 | return json_encode($record, JSON_UNESCAPED_UNICODE); |
231 | } |
232 | } |