@@ -13,6 +13,7 @@ class FileHandler
13
13
14
14
private array $ files = [];
15
15
16
+
16
17
/**
17
18
* @throws FileHandlerException
18
19
*/
@@ -160,31 +161,44 @@ public function delete(string $filename): void
160
161
/**
161
162
* @throws FileHandlerException
162
163
*/
163
- private function getRows (): Generator
164
+ private function getRows (string | null $ filename = null ): Generator
164
165
{
165
- if (count ($ this ->files ) > 1 ) {
166
- throw new FileHandlerException ("multiple files not allowed " );
167
- }
168
-
169
- $ file = $ this ->files [0 ];
170
- $ headers = fgetcsv ($ file );
171
-
172
- $ this ->isValidCsvFileFormat ($ headers );
166
+ $ file = $ this ->ensureSingleFileProcessing ($ filename );
167
+ $ headers = $ this ->extractHeader ($ file );
173
168
174
169
$ isEmptyFile = true ;
175
- while (($ row = fgetcsv ($ file )) !== false ) {
176
- $ isEmptyFile = false ;
177
- $ this ->isValidCsvFileFormat ($ row );
178
- $ item = array_combine ($ headers , $ row );
179
- yield $ item ;
170
+ try {
171
+ while (($ row = fgetcsv ($ file )) !== false ) {
172
+ $ isEmptyFile = false ;
173
+ $ this ->isValidCsvFileFormat ($ row );
174
+ $ item = array_combine ($ headers , $ row );
175
+
176
+ yield $ item ;
177
+ }
178
+ } finally {
179
+ fclose ($ file );
180
180
}
181
- fclose ( $ file );
181
+
182
182
183
183
if ($ isEmptyFile ) {
184
184
throw new FileHandlerException ('invalid file format ' );
185
185
}
186
186
}
187
187
188
+ private function ensureSingleFileProcessing (string |null $ filename ): mixed
189
+ {
190
+ if (count ($ this ->files ) < 1 ) {
191
+ if (!$ filename || !file_exists ($ filename )) {
192
+ throw new FileHandlerException ("no files to process " );
193
+ }
194
+ $ this ->open ($ filename );
195
+ }
196
+ if (count ($ this ->files ) > 1 ) {
197
+ throw new FileHandlerException ("multiple files not allowed " );
198
+ }
199
+ return $ this ->files [0 ];
200
+ }
201
+
188
202
/**
189
203
* @throws FileHandlerException
190
204
*/
@@ -198,6 +212,100 @@ private function search(string $keyword, string $column, string|null $format): b
198
212
return false ;
199
213
}
200
214
215
+ public function findAndReplaceInCsv (
216
+ string $ filename ,
217
+ string $ keyword ,
218
+ string $ replace ,
219
+ string |null $ column = null
220
+ ): bool {
221
+ $ headers = $ this ->extractHeader ($ filename );
222
+
223
+
224
+ if (!$ headers ) {
225
+ throw new FileHandlerException ('failed to extract header ' );
226
+ }
227
+
228
+ $ tempFilePath = $ this ->createTempFileWithHeaders ($ headers );
229
+
230
+ try {
231
+ $ count = 0 ;
232
+ foreach ($ this ->getRows ($ filename ) as $ row ) {
233
+ if (!$ column ) {
234
+ $ count += $ this ->replaceKeywordInRow ($ row , $ keyword , $ replace );
235
+ } else {
236
+ $ count += $ this ->replaceKeywordInColumn ($ row , $ column , $ keyword , $ replace );
237
+ }
238
+
239
+ $ this ->writeRowToTempFile ($ tempFilePath , $ row );
240
+ }
241
+
242
+ if ($ count < 1 ) {
243
+ return false ;
244
+ }
245
+
246
+ $ this ->renameTempFile ($ tempFilePath , $ filename );
247
+ } finally {
248
+ $ this ->cleanupTempFile ($ tempFilePath );
249
+ }
250
+
251
+ return true ;
252
+ }
253
+
254
+ private function replaceKeywordInRow (array &$ row , string $ keyword , string $ replace ): int
255
+ {
256
+ $ count = 0 ;
257
+ $ replacement = array_search ($ keyword , $ row );
258
+
259
+ if ($ replacement !== false ) {
260
+ $ row [$ replacement ] = $ replace ;
261
+ $ count ++;
262
+ }
263
+
264
+ return $ count ;
265
+ }
266
+
267
+ private function replaceKeywordInColumn (array &$ row , string $ column , string $ keyword , string $ replace ): int
268
+ {
269
+ $ count = 0 ;
270
+
271
+ if ($ keyword === $ row [$ column ]) {
272
+ $ row [$ column ] = $ replace ;
273
+ $ count ++;
274
+ }
275
+
276
+ return $ count ;
277
+ }
278
+
279
+ private function writeRowToTempFile (string $ tempFilePath , array $ row ): void
280
+ {
281
+ $ tempFileHandle = fopen ($ tempFilePath , 'a ' );
282
+ fputs ($ tempFileHandle , implode (', ' , $ row ) . PHP_EOL );
283
+ fclose ($ tempFileHandle );
284
+ }
285
+
286
+ private function renameTempFile (string $ tempFilePath , string $ filename ): void
287
+ {
288
+ if (!rename ($ tempFilePath , $ filename )) {
289
+ throw new FileHandlerException ('Failed to rename temp file ' );
290
+ }
291
+ }
292
+
293
+ private function cleanupTempFile (string $ tempFilePath ): void
294
+ {
295
+ unlink ($ tempFilePath );
296
+ }
297
+
298
+ private function createTempFileWithHeaders (array $ headers ): string
299
+ {
300
+ $ tempFilePath = tempnam (sys_get_temp_dir (), 'tempfile_ ' );
301
+ $ tempFileHandle = fopen ($ tempFilePath , 'w ' );
302
+ fputs ($ tempFileHandle , implode (', ' , $ headers ) . PHP_EOL );
303
+ fclose ($ tempFileHandle );
304
+
305
+ return $ tempFilePath ;
306
+ }
307
+
308
+
201
309
/**
202
310
* @throws FileHandlerException
203
311
*/
@@ -207,4 +315,28 @@ private function isValidCsvFileFormat(array|false $row): void
207
315
throw new FileHandlerException ('invalid file format ' );
208
316
}
209
317
}
318
+
319
+ private function extractHeader (mixed $ file ): array |false
320
+ {
321
+ if (is_resource ($ file )) {
322
+ $ headers = fgetcsv ($ file );
323
+ }
324
+ if (is_string ($ file )) {
325
+ if (!file_exists ($ file )) {
326
+ return false ;
327
+ }
328
+ try {
329
+ $ file = fopen ($ file , 'r ' );
330
+ $ headers = fgetcsv ($ file );
331
+ } finally {
332
+ fclose ($ file );
333
+ }
334
+ }
335
+
336
+ if ($ this ->isValidCsvFileFormat ($ headers ) !== false ) {
337
+ return $ headers ;
338
+ }
339
+
340
+ return false ;
341
+ }
210
342
}
0 commit comments