08-traitement-fichiers
<?php
class FileProcessor {
public function process(string $filePath): array {
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
$results = [];
if($extension === 'csv') {
$handle = fopen($filePath, 'r');
$headers = fgetcsv($handle);
$data = [];
while(($row = fgetcsv($handle)) !== false) {
$data[] = array_combine($headers, $row);
}
fclose($handle);
$validated = $this->validateData($data);
$transformed = $this->transformData($validated);
$db = new PDO('mysql:host=localhost;dbname=imports','root','');
foreach($transformed as $row) {
$stmt = $db->prepare('INSERT INTO records (name,email,amount) VALUES (?,?,?)');
$stmt->execute([$row['name'], $row['email'], $row['amount']]);
}
$results['imported'] = count($transformed);
$results['errors'] = [];
$this->sendNotification('CSV import complete', count($transformed) . ' records imported');
} elseif($extension === 'json') {
$content = file_get_contents($filePath);
$data = json_decode($content, true);
if(!$data) {
throw new Exception("Invalid JSON");
}
$validated = $this->validateData($data);
$transformed = $this->transformData($validated);
$db = new PDO('mysql:host=localhost;dbname=imports','root','');
foreach($transformed as $row) {
$stmt = $db->prepare('INSERT INTO records (name,email,amount) VALUES (?,?,?)');
$stmt->execute([$row['name'], $row['email'], $row['amount']]);
}
$results['imported'] = count($transformed);
$results['errors'] = [];
$this->sendNotification('JSON import complete', count($transformed) . ' records imported');
} elseif($extension === 'xml') {
$xml = simplexml_load_file($filePath);
$data = [];
foreach($xml->record as $record) {
$data[] = [
'name' => (string)$record->name,
'email' => (string)$record->email,
'amount' => (float)$record->amount
];
}
$validated = $this->validateData($data);
$transformed = $this->transformData($validated);
$db = new PDO('mysql:host=localhost;dbname=imports','root','');
foreach($transformed as $row) {
$stmt = $db->prepare('INSERT INTO records (name,email,amount) VALUES (?,?,?)');
$stmt->execute([$row['name'], $row['email'], $row['amount']]);
}
$results['imported'] = count($transformed);
$results['errors'] = [];
$this->sendNotification('XML import complete', count($transformed) . ' records imported');
} else {
throw new Exception("Format not supported: $extension");
}
$this->archiveFile($filePath);
$this->logProcessing($filePath, $results);
return $results;
}
private function validateData(array $data): array {
$valid = [];
foreach($data as $row) {
if(isset($row['email']) && filter_var($row['email'], FILTER_VALIDATE_EMAIL)) {
if(isset($row['amount']) && is_numeric($row['amount']) && $row['amount'] > 0) {
$valid[] = $row;
}
}
}
return $valid;
}
private function transformData(array $data): array {
return array_map(function($row) {
return [
'name' => ucwords(strtolower($row['name'] ?? 'Unknown')),
'email' => strtolower($row['email']),
'amount' => round($row['amount'], 2)
];
}, $data);
}
private function archiveFile(string $path): void {
$archivePath = '/var/archives/' . basename($path);
rename($path, $archivePath);
}
private function logProcessing(string $file, array $results): void {
$db = new PDO('mysql:host=localhost;dbname=imports','root','');
$stmt = $db->prepare('INSERT INTO processing_log (filename,imported_count,timestamp) VALUES (?,?,?)');
$stmt->execute([basename($file), $results['imported'], time()]);
}
private function sendNotification(string $subject, string $message): void {
mail('admin@example.com', $subject, $message);
}
}
/*
=== USER STORIES ===
US1: En tant que data analyst, je veux pouvoir importer des fichiers Excel (.xlsx)
en plus des formats existants.
US2: En tant que développeur, je veux tester la logique de validation et transformation
sans dépendre du système de fichiers ni de la base de données.
US3: En tant que product owner, je veux stocker les données importées dans MongoDB
au lieu de MySQL, selon la configuration.
US4: En tant que data manager, je veux pouvoir définir des règles de transformation
différentes selon le type de données (clients, produits, commandes) sans
modifier la classe FileProcessor.
US5: En tant que sysadmin, je veux recevoir les notifications via Slack au lieu
d'email, et logger dans Elasticsearch au lieu de MySQL.
US6: En tant que compliance officer, je veux pouvoir appliquer des validations
supplémentaires spécifiques à certains formats (RGPD, formats comptables)
sans toucher au code existant.
US7: En tant que product manager, je veux supporter le streaming de gros fichiers
(plusieurs Go) sans charger tout le fichier en mémoire.
*/