PHP exporter des données au format csv stocké dans une base MySQL

Intro

J’ai été amené à réaliser export de données au format csv à partir d’une base MySQL pour le compte d’un client.
Mon premier réflexe a été de voir du côté de phpmyadmin, puis d’utiliser la fonction d’export intégrée.
Après avoir réalisé la requête sql, je me suis confronté à une limite, il n’est pas possible d’exporter plus de 500 lignes à la fois … (je n’ai pas trouvé la manip pour exporter plus ???)
Deuxième réalité, je serai amené à réaliser plusieurs fois des exports et donc refaire la manipulation à chaque fois …
Pourquoi ne pas mettre en place un script PHP qui va permettre :

  • au client d’être autonome
  • et moi de réaliser rapidement la tâche ?

J’ai donc décidé de créer un bouton, dans une page html du backoffice , d’export des données.
Derrière le bouton, il y a une class php qui va réaliser les tâches suivantes :

  1. créer un fichier temporaire csv sur la machine
    • qui va contenir toutes colonnes définies dans un SELECT
    • dans le même dossier que le script de lancement index.php
  2. modifier l’entête http pour avoir la pop up d’upload du navigateur proposant le fichier csv
  3. supprimer le fichier temporaire
  4. utilisable avec deux technologies de connexion :
    • mysql_ (un peu old school mais je vois encore régulièrement cette technologie en action)
    • et PDO
  5. Attention : les champs de type VARBINARY et BLOG sont mal générés

La class UploadCSV

Voici la class PHP UploadCSV, dans le fichier class-exportcsv.php :

<?php 
class UploadCSV 
{
	protected $PdoDBConnection ;
	protected $outputfileName ;
	protected $pathCSV ;
	protected $sqlSelect ;
	protected $quoteColon ;
	protected $separator ;

	public function __construct(array $init){
		
		$default_init = array(
			'PdoDBConnection' => NULL,
			'outputfileName' => "export_" . date("Y-m-d-H-i-s") . ".csv",
			'pathCSV' => "./export-".uniqid() , 
			'sqlSelect' => NULL,
			'quoteColon' => NULL,
			'separator' => ",",
			);

		$init = array_merge($default_init, $init);

		$this->setDBConnection($init['PdoDBConnection']);
		$this->setoutputfileName($init['outputfileName']);
		$this->setpathCSV($init['pathCSV']);
		$this->setsqlSelect($init['sqlSelect']);
		$this->setquoteColon($init['quoteColon']);
		$this->setseparator($init['separator']);
	}

	private function mySqlSelect(){
		$result = array();

		$sql_all_client = $this->sqlSelect;

		$r = mysql_query($sql_all_client);

		while($row = mysql_fetch_object($r)){
			$result[] = $row;
		}
		return $result;
	}

	public function PdoSelect(){
		$result = array();

		$bdd = $this->PdoDBConnection ;
		
		$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
		$bdd->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

		$sql_all_client = $this->sqlSelect;
		
		$stmt = $bdd->prepare($sql_all_client);

		$stmt->execute();

		$r = $stmt->fetchAll();
		
		$stmt->closeCursor();
		$stmt=NULL;

		foreach ($r as $row) {
			$result[] = $row;
		}
		return $result;
	}

	private function csv_headers($outputfileName,$pathCSV) {
	    // disable caching
	    $now = gmdate("D, d M Y H:i:s");
	    header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
	    header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
	    header("Last-Modified: {$now} GMT");

	    // force download  
	    header("Content-Type: application/force-download");
	    header("Content-Type: application/octet-stream");
	    header("Content-Type: application/download");

	    // file weight
	    header("Content-Length: " . filesize($pathCSV));

	    // encrypt output in utf-8
	    header('Content-Type: text/html; charset=utf-8');

	    // disposition / encoding on response body
	    header("Content-Disposition: attachment;filename={$outputfileName}");
	    header("Content-Transfer-Encoding: binary");
	}

	private function createcsv($pathCSV,$data){

		$myfile = fopen($pathCSV, 'w');

		/*générer la ligne de titre*/
		$title = $data[0];
		$txt = "";
		$j=0;
		foreach ($title as $key => $value) {
			if($this->quoteColon == "*"){
				$txt .= "'".$key.$this->separator;

			}elseif (is_array ($this->quoteColon)){
				
				if(in_array($j,$this->quoteColon)){

					$txt .= "'".$key.$this->separator;
				
				}else{
					$txt .= $key.$this->separator;
				}

			}else{
				$txt .= $key.$this->separator;
			}
			$j++;
		}
		$this->cleanBrWrite($myfile, $txt);
		/*fin générer la ligne de titre*/

		/*générer les lignes de corps*/
		foreach ($data as $d) {
				$txt = "";
				$j=0;
				foreach ($d as $i) {
					if($this->quoteColon == "*"){
						$txt .= "'".$i.$this->separator;

					}elseif (is_array ($this->quoteColon)){

						if(in_array($j,$this->quoteColon)){
							$txt .= "'".$i.$this->separator;
						}else{
							$txt .= $i.$this->separator;
						}
					}else{
						$txt .= $i.$this->separator;
					}
					$j++;
				}

				$this->cleanBrWrite($myfile, $txt);
		}
		/*fin générer les lignes de corps*/

		fclose($myfile);
	}

	private function cleanBrWrite($file, $txt){
		/*supprimer la dernière virgule de la ligne*/
		$txt = rtrim($txt, ",");
		/*remplacer les retours chariots pr un espace*/
		$txt = str_replace(array("\r", "\n"), ' ', $txt);

		/*remplacer les ; par un rien (Excel interprète ; comme des tabulation)*/
		$txt = str_replace(array(";"), '', $txt);
		/*Passer à la ligne*/
		$txt = $txt."\n";
		fwrite($file, $txt);
	}

	public function generate(){
		/*récupérer les données */
		if($this->PdoDBConnection){
			$data = $this->PdoSelect();
		}else{
			$data = $this->mySqlSelect();
		}

		/*création du fichier csv dans le serveur */
		$this->createcsv($this->pathCSV , $data);

		/*changer le header HTTP */
		$this->csv_headers($this->outputfileName , $this->pathCSV);

		/*lire le fichier csv */
		readfile($this->pathCSV);

		/*supprimer le fichier csv du serveur */
		
		unlink($this->pathCSV);
		
		die();
	}

	private function setDBConnection($info){
		if($info){
			$this->PdoDBConnection = $info;
		}
	}

	private function setoutputfileName($info){
		if($info){
			$this->outputfileName = $info;
		}else{
			die('outputfileName - la valeur ne peut pas etre null');
		}
	}

	private function setpathCSV($info){
		if($info){
			$this->pathCSV = $info;
		}else{
			throw new Exception('pathCSV - la valeur ne peut pas etre null');
		}
	}

	private function setsqlSelect($info){
		if($info){
			$this->sqlSelect = $info;
		}else{
			throw new Exception('sqlSelect - la valeur ne peut pas etre null');
		}
	}

	private function setquoteColon($info){
		if($info){
			$this->quoteColon = $info;
		}else{
			$this->quoteColon = NULL;
		}
	}

	private function setseparator($info){
		if($info){
			$this->separator = $info;
		}else{
			throw new Exception('separator - la valeur ne peut pas etre null');
		}
	}
}

Utiliser la class UploadCSV avec mysql_connect

Dans le fichier index.php :

<?php
require_once 'class-exportcsv.php';

$sql = 'SELECT * FROM `clients` ORDER BY `id_client` ASC';

$host = 'localhost';
$bdname = 'bdd_name';
$user = 'root';
$password = '';

$link = mysql_connect($host,$user, $password);

if (!$link) {
    die('Could not connect: ' . mysql_error());
}

$BddSelect = mysql_select_db ( $bdname , $link );

if (!$BddSelect) {
    die('Could not connect: ' . mysql_error());
}

$init = array(
	'PdoDBConnection' => NULL, // obligatoire
	'sqlSelect' => $sql, // obligatoire à définir
	'outputfileName' => "client_" . date("Y-m-d-H-i-s") . ".csv", // par défaut "export_" . date("Y-m-d-H-i-s") . ".csv"
	'pathCSV' => "./txt-".uniqid() , // par défaut "./export-".uniqid()
	'quoteColon' => array(4,11), // par défaut NULL - * tous les champs avec quote - certaine colonne array(1,2) à partir de 0
	'separator' => "|", // par défaut ,
	); 

$csv = new UploadCSV($init);
$csv->generate();

Utiliser la class UploadCSV avec PDO

Dans le fichier index.php :

<?php
require_once 'class-exportcsv.php';

$sql = 'SELECT * FROM `clients` ORDER BY `id_client` ASC';

$host = 'localhost';
$bdname = 'bdd_name';
$user = 'root';
$password = '';


$init = array(
	'PdoDBConnection' => new PDO('mysql:host='.$host.';dbname='.$bdname, $user, $password), // obligatoire
	'sqlSelect' => $sql, // obligatoire à définir
	'outputfileName' => "client_" . date("Y-m-d-H-i-s") . ".csv", // par défaut "export_" . date("Y-m-d-H-i-s") . ".csv"
	'pathCSV' => "./txt-".uniqid() , // par défaut "./export-".uniqid()
	'quoteColon' => array(4,11), // par défaut NULL - * tous les champs avec quote - certaine colonne array(1,2) à partir de 0
	'separator' => "|", // par défaut ,
	); 

$csv = new UploadCSV($init);
$csv->generate();