[php5.2] classe pdo

Description

Ci-joints les fichiers et ce qu'ils font :

- PDO2.classe.php: Création d'un singleton par extension de la classe PDO (reprise existant),
- PDO3.classe.php: Lister les tables de la base de données par ordre de taille,
- PDO3.classe.php: Lister les champs d'une table soit en mode résumé, soit détaillé
- PDO3.classe.php: Insérer facilement un tableau de données dans la table souhaitée
- test_pdo.php : Montrer comment la classe et ses méthodes s'utilisent.
- fonctions_classe.php: c'est simplement la fonction d'autoload des classes à leur instanciation (SPL)

Les méthodes utilisées requièrent pour la plupart au minimum PHP5.1, donc vous pouvez essayer avec cette version, mais j'ai tout testé avec PHP5.2.9.

Bon, c'est ma première classe POO en PHP5, donc ce n'est pas aussi transcendant que quelques autres que j'ai vues sur ce site, mais elle fonctionne bien et me parait simple à améliorer et très pratique d'utilisation.

Source / Exemple :


// Je ne place QUE la classe PDO3 pour un accès direct, mais il faut télécharger les autres.

<?php

/**



class PDO3 extends PDO2 {

	/* Variables de la classe */
	protected $_pdo		= NULL;				// ne doit pas être "private" car héritage de PDO2
	private $_delimiter	= ',   '; 			// ne pas modifier !
	private $_debug		= false; 			// MODIF si vous voulez
	
	
	/* Constructeur : héritage public obligatoire par héritage de PDO */
	public function __construct($pdo)
	{
		if (isset($pdo) ) {
			$this->_pdo = $pdo;
			$this->checkPdoExtension();
		}
		else {
			throw new PDOException("No pdo connection");
		}
	}

	
	/* Destructeur */
	public function __destruct() // nota: ne surtout pas mettre de parametre
	{
		$this->close();
	}

	
	
	/** 28/02/2010

  • ferme la connexion à la dB (portage dans la classe PDO de la fonction mysql_close)
  • /
public function close() { $this->_pdo = NULL; } /* Methode magique */ public function __toString($object) { $string = (string) $object ; return $string; } /** public function getFieldsSummary($limit = false) { $data_array = array(); $limit = ($limit == false)? '' : 'LIMIT '. (int) $limit; // Les tables, dans l'ordre = f(taille) $sql = "SELECT TABLE_NAME, ROUND((DATA_LENGTH + INDEX_LENGTH) / (1024 * 1024), 2) AS SIZE_MO, TABLE_ROWS, AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '". $this->_dbname . "' ORDER BY DATA_LENGTH + INDEX_LENGTH DESC ". $limit; $result = $this->_pdo->query($sql); $rows = $result->fetchAll(PDO::FETCH_ASSOC); foreach($rows as $key => $value) { $data_array[$key] = $value; } return $data_array; } /** public function getFieldsDetails($limit = false) { $data_array = array(); $limit = ($limit == false)? false : (int) $limit; // Summary: reprise de ces infos comme base de recherche des détails $source_array = $this->getFieldsSummary($limit); // Details foreach( $source_array as $key => $field_datas) { if (is_array($field_datas)) { // Transfert du Summary dans le tableau de détails $data_array[$key] = $field_datas; // Details de chaque table $table_encours = $field_datas['TABLE_NAME']; $sql = "SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '". $this->_dbname . "' AND TABLE_NAME = '" . $table_encours . "'"; $result = $this->_pdo->query($sql); $rows = $result->fetchAll(PDO::FETCH_ASSOC); // Field List $field_list= ''; $memo_auto_inc = '' ; $count = count($rows); foreach($rows as $k => $value) { $memo_auto_inc = ($value['EXTRA'] == 'auto_increment')? $value['COLUMN_NAME'] : $memo_auto_inc ; $field_list .= '<b>'. $value['COLUMN_NAME'] . '</b> (' . $value['COLUMN_TYPE'] . ') ' . ' (' . $value['COLUMN_KEY'] . ')'. $this->_delimiter; } // Marqueur éventuel sur l'AI $data_array[$key]['AI_RESET_NEED'] = ($data_array[$key]['AUTO_INCREMENT'] > $count) ? 'true' : 'false' ; $data_array[$key]['AI_FIELD'] = $memo_auto_inc ; // Array Push $data_array[$key]['FIELDS_COUNT'] = $count; $data_array[$key]['FIELDS_LIST'] = $field_list; } } return $data_array; } /** 28/02/2010 portage dans la classe PDO de la fonction db_pdo.php
  • retourne la liste des champs d'une table sous forme de vecteur colonne
  • @param : (string) $table_name nom de la table dont on veut la liste des champs
  • @return : (array) $fieldNames liste des noms de champs de $table_name
  • /
public function getFieldsName($table_name) { // Field Details: reprise de ces infos comme base de recherche des détails $source_array = $this->getFieldsDetails(false); // 1ere méthode: lourde et peu élégante /* $fieldNames = array() ; foreach ($source_array as $key => $table_datas) { if (is_array($table_datas)) { if ($table_datas['TABLE_NAME'] == $table_name) { $fieldNames = explode( $this->_delimiter , $table_datas['FIELDS_LIST'] ) ; } } } array_pop($fieldNames) ;
  • /
// 2eme méthode: plus directe et élégante : recupération récursive de la clé correspondante à la table $key = $this->recursiveArraySearch($table_name , $source_array ) ; $fieldNames = explode( $this->_delimiter , $source_array[$key]['FIELDS_LIST'] ) ; array_pop($fieldNames); // 3eme methode: brut accès direct aux infos dB sans utiliser d'autres fonctions /* $recordset = $this->_pdo->query("SHOW COLUMNS FROM $table_name"); $fields = $recordset->fetchAll(PDO::FETCH_ASSOC); foreach ($fields as $field) { $fieldNames[] = $field['Field']; }
  • /
return $fieldNames; } /** public function resetAI($table_name, $init_AI = 200) { // 1) Vérifier que cette table n'est pas liée à d'autres par ses ID et à appliquer uniquement après suppression de blocs de datas... // 2) Combien de datas dans la table // 3) Quelle valeur de AI initial // 4) Quelle valeur de AI courante // 5) Justification de remettre AI à flot // 6) Reset des AI existants // 7) Mise à jour de l'AI de cette table } /** 28/02/2010 portage dans la classe PDO de la fonction incluse dans db_pdo.php
  • sauve des datas (variables ou array) INSERT ou UPDATE dans la table $table de la dB
  • @NOTA : fonction dépréciée au profit de la fonction saveData
  • @param : $table_name, $data_array
  • @return : true si tout s'est bien passé, false sinon
  • @doc : http://fr.php.net/manual/fr/function.mysql-query.php#84962
  • /
public function addData($table_name, $excluded_fields = '', $data_array, $sql_type = 'insert', $sql_condition = NULL ) { // Init $fields = ''; $values = ''; $debug = $this->_debug ; $pattern = '/tel|phone|fax|siret|number|numero/'; // format input fields into sql (explode du tableau associatif en requete SQL) foreach ($data_array as $field => $value) { if (!strstr($excluded_fields, $field)) { // construction de la sql pour INSERT if (strtolower($sql_type) == 'insert') { $fields .= "$field, "; $values .= "'$value', "; } // construction de la sql pour UPDATE else { if (!is_numeric($value)) { $fields .= "$field = '$value', "; // string } else { // exceptions: pour le num de tel/fax/siret qui peut commencer par un zero et peut ainsi être assimilé à un numeric, ce qui n'est pas le cas ds la dB if ( !preg_match( $pattern, $field ) ) { $fields .= "$field = $value, "; // numeric } else { $fields .= "$field = '$value', ";// si tel/phone/fax/siret => string } } } } } // remove trailing ", " from $fields and $values $fields = preg_replace('/, $/', '', $fields); $values = preg_replace('/, $/', '', $values); // create sql statement if (strtolower($sql_type) == 'insert') { $sql = "INSERT INTO $table_name ($fields) VALUES ($values)"; } elseif (strtolower($sql_type) == 'update') { if (!isset($sql_condition)) { echo 'ERROR: You must enter a sql condition!'; exit; } $sql = "UPDATE $table_name SET $fields WHERE $sql_condition"; } else { echo 'ERROR: Invalid input for argument $sql_type: must be "insert" or "update"'; exit; } // prepare & execute sql $query = $this->_pdo->prepare($sql) ; $result = $query->execute() ; //$result->closeCursor(); // not always necessary: depends on SQL driver needs if ( $result ) { return true; } else if ( $result === false ){ if ($debug) { return 'err sql: ' ; //. mysql_error(); } else { return false; } } } /* fin addData */ /** 01/03/2010
  • sauve $data_array dans $table (la condition d'UPDATE devenant optionnelle si elle ne porte que sur l'id PK)
  • @param : $table_name destination, $data_array à sauver, $sql_type='insert' ou 'update', $optional_condition
  • @doc : http://www.php.net/manual/fr/pdostatement.execute.php#80499
  • @return : true si tout s'est bien passé, false sinon
  • /
public function saveData($table_name, $data_array, $sql_type='insert', $optional_condition=NULL ) { // Init $debug = $this->_debug; $sql =''; $pattern_field = '/^(?:\<b\>)(\w+)(?:\<\/b\>)(?:\s{0,})\((.+)\)(?:\s{0,})\((.+)?\)$/'; $field_name = $field_format = $field_key = array() ; // Initial checks // Data_array in line with table fields // db fields $db_fields = $this->getFieldsName($table_name) ; if ( empty($db_fields) ) return false ; foreach ( $db_fields as $field) { unset($matches); if ( preg_match($pattern_field, $field, $matches) ) { $field_name[] = trim($matches[1]); $field_format[] = trim($matches[2]); $field_key[] = trim($matches[3]); } } /* if ($debug) { $this->arr_dump(array('keys'=>$field_key, 'names'=>$field_name, 'formats'=>$field_format) , 'Matching results'); }
  • /
// Comparison $db_fields with $data_array: can accept missing fields but no mismatching fields (thus avoiding typos in $data_array) $mismatching_fields = array_diff(array_keys($data_array), $field_name); if ( count($mismatching_fields) != 0) { if ($debug) { echo '<br/>PDO3 db Vs data fields issue'; $this->arr_dump($mismatching_fields, 'mismatch' ); } return false; } // Check data_array format Vs dB_fields format // ######### // # To do // ######### // Build SQL statement // INSERT $sql = "INSERT INTO $table_name ($fields) VALUES ($values)"; $fields = ''; $values = ''; if ( strtolower($sql_type) == 'insert') { foreach($data_array as $field => $data) { $fields .= "$field, "; $values .= "'$data', "; } $fields = preg_replace('/, $/', '', $fields); $values = preg_replace('/, $/', '', $values); $sql = "INSERT INTO $table_name ($fields) VALUES ($values)"; if ($debug) echo '<br/>sql statement: ' . $sql; } // UPDATE if ( strtolower($sql_type) == 'update') { //if ($debug) echo '<br/>Starting update....' ; // Update Condition $sql_condition $sql_condition = ''; // dB primary key field required $needle = 'PRI'; $pk_key = trim($this->recursiveArraySearch($needle, $field_key) ) ; if (false === $pk_key ) { // NOTA: '===' is a must as $pk_key canb be 0 and then understood as 'false' if ($debug) { echo '<br/>PDO3 Err: no primary key found' ; $this->arr_dump( array('pk key'=> $pk_key , 'needle'=>$needle, 'fields' => $field_key), 'Check needle & arrays') ; } return false; } else { if ($debug) { echo '<br/>found primary key in db datas array <b>keys</b> :' . $pk_key; // $this->arr_dump( $field_key , 'field_key'); } } // data_array field content check $pk_value = (array_key_exists($field_name[$pk_key], $data_array))? $data_array[$field_name[$pk_key]] : false ; if (false === $pk_value || empty($pk_value) ) { if ($debug) $this->arr_dump( array('key'=>$pk_key, 'value'=>$pk_value, 'data'=>$data_array) , 'PDO3 Err: no data content for primary key sql condition'); return false ; } else if ($debug) echo '<br/>found primary key value in db datas array <b>values</b>:' . $pk_value; // db_field: check if table field of row(pk) exists (in order to being able to properly set the sql_condition) // ######### // # to do // ######### // Sql condition base $sql_condition = $field_name[$pk_key] . '=' . $pk_value; if ($debug) echo '<br/>sql update condition base: ' . $sql_condition ; // Sql condition options // ######### // # to do: check if necessary (prepared request) that the optional sql condition is not matching with forbidden conditions like 1=1 or DELETE.. // ######### $sql_condition .= ($optional_condition)? ' AND ' . $optional_condition : '' ; // Build sql statement (not necessary to remove PK field from fields to update) $fields = ''; foreach($data_array as $field => $data) { $fields .= "$field = '$data', "; } // remove trailing ',' $fields = preg_replace('/, $/', '', $fields); // Sql statement $sql = "UPDATE $table_name SET $fields WHERE $sql_condition"; if ($debug) echo '<br/>sql statement: ' . $sql; } // Prepare & Execute Sql statement (sothat there is no need to use "quote" method for data protection against SQL injection) if ($sql) { $query = $this->_pdo->prepare($sql) ; $result = $query->execute() ; $count = $query->rowCount(); // Check affected rows by INSERT or UPDATE (nota : this methode does not work for SELECT) } // Return Value if ($result && $count > 0) { return true; } else{ return false; } } /* fin saveData */ /** private function recursiveArraySearch($needle, $haystack) { foreach($haystack as $key=>$value) { $current_key=$key; if($needle===$value OR (is_array($value) && $this->recursiveArraySearch($needle, $value) !== false)) { return $current_key; } } return false; } /** 01/02/2010 // ######### à ré-implémenter en amont dans PDO2 avec gestion erreur ################ private function checkPdoExtension() { if ($this->_debug) echo '<br/>Checking for PDO extension loaded'; $pdo_ext = false; foreach(get_loaded_extensions() as $extension) { if(strpos(strtolower($extension), 'pdo') !== 'pdo') { $pdo_ext = true; } } if ($this->_debug && false == $pdo_ext) echo '<br/>PDO extension not loaded'; return $pdo_ext; } /**
  • fonction de debugage
  • /
private function arr_dump($array, $text='') { echo '<br/>Dump('.$text.')<pre>'; print_r($array); echo '</pre>' ; } } /* fin de la Classe */ ?>

Conclusion :


J'attends avec un frémissement non dissimulé les critiques constructives :-)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.