[php5] smartdir : les itérateurs en php - lecture intelligente de répertoire

Soyez le premier à donner votre avis sur cette source.

Vue 7 963 fois - Téléchargée 337 fois

Description

J'ai décidé de montrer toute la puissance de la SPL (Standard PHP Library) et des itérateurs en PHP5.
Ce package est à personnaliser. Il permet la lecture récursive ou non de répertoire, en appliquant, ou non, des filtres.
FILTER_ON fait un filtre montrant uniquement les fichiers correspondant aux masques :
oSmartDir::FILTER_ON = 'php';
Va filtrer sur les fichiers contenant'php'.
oSmartDir::FILTER_ON = array ('php, 'html', 'js');
va filtrer sur les fichiers contenant php, html, ou js.

FILTER_OFF faut un filtre négatif :
oSmartDir::FILTER_OFF = 'php';
ne montrera que les fichiers ne contenant pas 'php'.
Peut aussi être un tableau de masques.

oSmartDir::DIr = false;
Ne lira pas les répertoire

oSmartDir::FILE = false;
ne montrera pas les fichiers

oSmartDir::RECURSE = false;
Ne lira pas récursivement.

On peut évidemment modifier ces filtres via la méthode myFilter::valid ()

Méthodes :
oSmartDir::getDir ();
permet simplement de récupérer l'itérateur pour l'arborescence définie via les filtres (ou non, d'ailleurs).
Plus tard, j'implémenterai de nouvelles méthodes "outils" : copie, déplacement etc...en tenant compte des filtres.
//////
J'ai mis une méthode copy ().
Attention, elle est effective sur le fichier d'exemple : le zip contient toute une arborescence. Le fichier exemple lit cette arbo avec divers filtres, pour donner quelques exemples, puis crée un répertoire copie/ dans lequel il copie toute l'arborescence, mais en ne prenan t en compte que les fichiers contenant 'php'.
//////

J'ai volontairement montré un package assez complexe...mais la lecture d'un répertoire de manière récursive peut se faire en 3 lignes grâce aux itérateurs :
<?php

$dir = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('.'), true);

foreach ( $dir as $file ) {
echo str_repeat('-', $dir->getDepth()) . ' '.$file.'<br />';
}
?>
Essayez ce code : il va lire récursivement le répertoire courant (donc, avec ses sous-répertoires), tout en indentant en fonction de l'arborescence.
Si si...!

Source / Exemple :


<?php
/**

  • smartDir package
  • @author Johan Barbier <johan.barbier@gmail.com>
  • @version 20011120
  • /
/**
  • class myFilter
  • check active filters
  • RecursiveFilterIterator child
  • /
class myFilter extends RecursiveFilterIterator { /**
  • active filter ON (keep these)
*
  • @var mixed
  • /
private $mFilterOn; /**
  • active filter OFF (do not keep these)
*
  • @var mixed
  • /
private $mFilterOff; /**
  • iterator
*
  • @var iterator
  • /
private $it = null; /**
  • error message
*
  • @var string
  • /
const ERROR_NO_VALID_FILTER = '{__FILTER__} is not a valid setable filter'; /**
  • Constructor
*
  • @param iterator $it
  • @param mixed $mFilterOn
  • @param mixed $mFilterOff
  • /
public function __construct ($it, $mFilterOn, $mFilterOff) { parent::__construct ($it); $this -> it = $it; $this -> mFilterOn = $mFilterOn; $this -> mFilterOff = $mFilterOff; } /**
  • Function accept ()
  • returns true or false if the current element is accepted or not
*
  • @return boolean
  • /
public function accept () { if (!is_null ($this -> mFilterOn)) { if (!is_array ($this -> mFilterOn)) { $mPos = strpos ($this -> it -> getFileName (), (string)$this -> mFilterOn); if (false === $mPos) { return false; } } else { foreach ($this -> mFilterOn as $sFilter) { $mPos = strpos ($this -> it -> getFileName (), (string)$sFilter); if (false !== $mPos) { return true; } } return false; } } if (!is_null ($this -> mFilterOff)) { if (!is_array ($this -> mFilterOff)) { $mPos = strpos ($this -> it -> getFileName (), (string)$this -> mFilterOff); if (false !== $mPos) { return false; } } else { foreach ($this -> mFilterOff as $sFilter) { $mPos = strpos ($this -> it -> getFileName (), (string)$sFilter); if (false !== $mPos) { return false; } else { return true; } } return false; } } return true; } } /**
  • Class MyRecursiveDirectoryIterator
  • parent of main class, RecursiveDirectoryIterator child
*
  • /
class MyRecursiveDirectoryIterator extends RecursiveDirectoryIterator { /**
  • are dir valid
*
  • @var boolean
  • /
protected $bDir = true; /**
  • are files valid
*
  • @var boolean
  • /
protected $bFile = true; /**
  • filter ON (keep these)
*
  • @var mixed
  • /
protected $mFilterOn = null; /**
  • filter OFF (do not keep these)
*
  • @var mixed
  • /
protected $mFilterOff = null; /**
  • Recursive dir or not
*
  • @var boolean
  • /
protected $bRecurse = true; /**
  • props that can be set
*
  • @var array
  • /
protected $aCanBeSet = array ( 'FLAG', 'FILTER_ON', 'FILTER_OFF', 'DIR', 'FILE', 'PATH', 'RECURSE' ); /**
  • filter ON (keep these)
*
  • @var RecursiveDirectoryIterator class constant
  • /
protected $cFlag = 0; /**
  • path to be read
*
  • @var string (valid path)
  • /
protected $sPath; protected $sCreatedDir = null; /**
  • error messages
*
  • @var string
  • /
const ERROR_PATH_NOT_FOUND = '{__PATH__} has not been found'; const ERROR_PROP_NOT_SETABLE = '{__PROP__} is not a setable property'; const ERROR_BAD_PROP_VALUE = '{__VAL__} is not a correct value for {__PROP__}'; const ERROR_NO_BOOLEAN = '{__PROP__} value must be a boolean'; /**
  • getChildren will retrieve sub path
*
  • @return subiterator
  • /
public function getChildren () { $iSub = new self ($this -> getPathname ()); $iSub -> bDir = $this -> bDir; $iSub -> bFile = $this -> bFile; $iSub -> mFilterOn = $this -> mFilterOn; $iSub -> mFilterOff = $this -> mFilterOff; return $iSub; } /**
  • get current key
*
  • @return string
  • /
public function key () { return $this -> getPath (); } /**
  • get current file
*
  • @return string
  • /
public function current () { return $this -> getFileName (); } /**
  • is the current element valid or not
*
  • @return boolean
  • /
public function valid () { if (!is_null ($this -> sCreatedDir)) { if ($this -> current () === $this -> sCreatedDir) { return false; } } $oFilter = new myFilter ($this, $this -> mFilterOn, $this -> mFilterOff); if (true === parent::valid ()) { if (false === $oFilter -> accept ()) { if (false === $this -> isDir ()) { parent::next (); return $this -> valid (); } else { return true; } } if (false === $this -> bDir) { if (true === $this -> isDir ()) { parent::next (); return $this -> valid (); } } if (false === $this -> bFile) { if (true === $this -> isFile ()) { parent::next (); return $this -> valid (); } } return true; } return false; } /**
  • Setter
*
  • @param string $sProp
  • @param mixed $mVal
  • /
public function __set ($sProp, $mVal) { if (!in_array ($sProp, $this -> aCanBeSet)) { throw new Exception (str_replace ('{__PATH__}', $sProp, self::ERROR_PROP_NOT_SETABLE)); } switch ($sProp) { case 'FLAG' : if (!in_array ($mVal, array (parent::CURRENT_AS_FILEINFO, parent::KEY_AS_FILENAME, parent::NEW_CURRENT_AND_KEY, 0))) { throw new Exception (str_replace (array ('{__VAL__}', '{__PROP__}'), array ($mVal, $sProp), self::ERROR_PATH_NOT_FOUND)); } $this -> cFlag = $mVal; break; case 'FILTER_ON' : $this -> mFilterOn = $mVal; $this -> mFilterOff = null; break; case 'FILTER_OFF' : $this -> mFilterOff = $mVal; $this -> mFilterOn = null; break; case 'DIR' : if (!is_bool ($mVal)) { throw new Exception (str_replace ('{__PROP__}', $sProp, self::ERROR_NO_BOOLEAN)); } $this -> bDir = $mVal; break; case 'FILE' : if (!is_bool ($mVal)) { throw new Exception (str_replace ('{__PROP__}', $sProp, self::ERROR_NO_BOOLEAN)); } $this -> bFile = $mVal; break; case 'RECURSE' : if (!is_bool ($mVal)) { throw new Exception (str_replace ('{__PROP__}', $sProp, self::ERROR_NO_BOOLEAN)); } $this -> bRecurse = $mVal; break; case 'PATH' : if (!is_dir ($mVal)) { throw new Exception (str_replace ('{__PATH__}', $mVal, self::ERROR_PATH_NOT_FOUND)); } $this -> sPath = $mVal; break; } } } /**
  • Class oSmartDir
  • MyRecursiveDirectoryIterator child
*
  • /
class oSmartDir extends MyRecursiveDirectoryIterator { /**
  • Constructor
*
  • @param string $sPath (valid path)
  • /
const ERROR_COPY_FAILED = 'Failed to copy {__FROM__} to {__TO__}'; public function __construct ($sPath) { if (!is_dir ($sPath)) { throw new Exception (str_replace ('{__PATH__}', $sPath, self::ERROR_PATH_NOT_FOUND)); } $this -> sPath = $sPath; } /**
  • getDir will retrieve the asked directory
*
  • @return iterator
  • /
public function getDir () { parent::__construct ($this -> sPath, $this -> cFlag); if (true === $this -> bRecurse) { return new RecursiveIteratorIterator ($this, true); } else { return $this; } } public function copy ($sTo) { $aDir = $this -> getDir (); if (!is_dir ($sTo)) { mkdir ($sTo, '0755'); } $this -> sCreatedDir = $sTo; while ($aDir -> valid ()) { if ($aDir -> current () !== $sTo && !$aDir -> isDot ()) { if (!$aDir -> isDir ()) { if (!@copy ($aDir -> getPathName (), $sTo.'/'.$aDir -> getPathName ())) { throw new Exception (str_replace (array ('{__FROM__}', '{__TO__}'), array ($aDir -> getPathName (), $sTo.'/'.$aDir -> getPathName ()), self::ERROR_COPY_FAILED)); } } else { if (!@mkdir ($sTo.'/'.$aDir -> getPathName (), '0755')) { throw new Exception (str_replace (array ('{__FROM__}', '{__TO__}'), array ($aDir -> getPathName (), $sTo.'/'.$aDir -> getPathName ()), self::ERROR_COPY_FAILED)); } } } $aDir -> next (); } $this -> sCreatedDir = null; } } /**
  • STARTING EXAMPLES
  • /
try { $oDir = new oSmartDir ('.'); } catch (Exception $e) { echo $e -> getMessage (); } try { echo '<br /><strong>ALL NO RECURSIVE</strong><br />'; $oDir -> RECURSE = false; // no recursivity $aDir = $oDir -> getDir (); while ($aDir -> valid ()) { if ($aDir -> isDot ()) { $aDir -> next (); } $sHtml = ''; if ($aDir -> isDir ()) { $sHtml.= '<strong>'.$aDir -> current ().'</strong>'; } else { $sHtml.= '<em>'.$aDir -> current ().'</em>'; } $sHtml .= '<br />'; echo $sHtml; $aDir -> next (); } echo '<br /><strong>ALL</strong><br />'; $oDir -> RECURSE = true; // recursivity back to true $aDir = $oDir -> getDir (); while ($aDir -> valid ()) { if ($aDir -> isDot ()) { $aDir -> next (); } $sHtml = str_repeat ('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', $aDir -> getDepth ()); if ($aDir -> isDir ()) { $sHtml.= '<strong>'.$aDir -> current ().'</strong>'; } else { $sHtml.= '<em>'.$aDir -> current ().'</em>'; } $sHtml .= '<br />'; echo $sHtml; $aDir -> next (); } echo '<br /><strong>ALL + fileSize</strong><br />'; $oDir -> RECURSE = true; // recursivity back to true $aDir = $oDir -> getDir (); while ($aDir -> valid ()) { if ($aDir -> isDot ()) { $aDir -> next (); } $sHtml = str_repeat ('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', $aDir -> getDepth ()); if ($aDir -> isDir ()) { $sHtml.= '<strong>'.$aDir -> current ().'</strong>'; } else { $sHtml.= '<em>'.$aDir -> current ().' '.round (($aDir -> getSize()/1024), 2).' Ko</em>'; } $sHtml .= '<br />'; echo $sHtml; $aDir -> next (); } echo '<br /><strong>ALL</strong><br />'; $aDir = $oDir -> getDir (); foreach ($aDir as $sK => $sV) { echo str_repeat ('-----', $aDir -> getDepth()), $sK, ' => ', $sV, '<br />'; } echo '<br /><strong>FILTRE ON SUR PHP</strong><br />'; $oDir -> FILTER_ON = 'php'; // only shows files with "php" in them $aDir = $oDir -> getDir (); foreach ($aDir as $sK => $sV) { echo str_repeat ('-----', $aDir -> getDepth()), $sK, ' => ', $sV, '<br />'; } echo '<br /><strong>DIR = FALSE</strong><br />'; $oDir -> DIR = false; // No directory $oDir -> FILTER_ON = null; // no filter ON $aDir = $oDir -> getDir (); foreach ($aDir as $sK => $sV) { echo str_repeat ('-----', $aDir -> getDepth()), $sK, ' => ', $sV, '<br />'; } echo '<br /><strong>FILE = FALSE</strong><br />'; $oDir -> DIR = true; // show directories $oDir -> FILE = false; // No files $aDir = $oDir -> getDir (); foreach ($aDir as $sK => $sV) { echo str_repeat ('-----', $aDir -> getDepth()), $sK, ' => ', $sV, '<br />'; } echo '<br /><strong>PATH = bla ET FILTRE OFF SUR PHP</strong><br />'; $oDir -> PATH = 'bla'; // parth = 'bla' $oDir -> FILE = true; // show files $oDir -> FILTER_OFF = 'php'; // do not show files with "php" in them $aDir = $oDir -> getDir (); foreach ($aDir as $sK => $sV) { //echo $sK, ' => ', $sV, '<br />'; echo str_repeat ('-----', $aDir -> getDepth()), $sK, ' => ', $sV, '<br />'; } /**
  • Create a copy of the whole directory recursively, but ONLY copy files with php in them ;-)
  • /
$oDir -> FILTER_ON = 'php'; $oDir -> PATH = '.'; $oDir -> copy ('copie'); } catch (Exception $e) { echo $e -> getMessage (), ' => ', $e -> getLine (); } ?>

Codes Sources

A voir également

Ajouter un commentaire Commentaires
kankrelune Messages postés 1293 Date d'inscription mardi 9 novembre 2004 Statut Membre Dernière intervention 21 mai 2015
20 nov. 2006 à 13:05
Ca à l'air pô mal du tout tout ça... faudra que je teste... .. .

Je connais pas trop les iterator de php5... faut dire que je suis à la traine en ce qui concerne php5... .. . :oS

@ tchaOo°
malalam Messages postés 10839 Date d'inscription lundi 24 février 2003 Statut Membre Dernière intervention 2 mars 2010 25
20 nov. 2006 à 13:37
C'est très, très puissant...mais très, très complexe lol. Très!
J'ai oublié de mettre un lien bien pratique :
http://www.php.net/~helly/php/ext/spl/main.html

Il montre toutes les implémentations des nombreuses classes de la SPL (bien plus que l'on peut en trouver sur la doc "normale", quelques exemples, et des liens très intéressants vers divers tuto.

Cet exemple n'est certainement pas optimisé, et ne tire certainement pas partie de toute la puissance des itérateurs. Mais bon, j'y travaille, je trouve ça intéressant et du coup, je veux faire partager mes découvertes :-)
cs_jean84 Messages postés 449 Date d'inscription jeudi 26 août 2004 Statut Membre Dernière intervention 5 mars 2009
20 nov. 2006 à 18:53
Salut !

Concretement, c'est quoi un iterateur ? J'en avais entendu parler en ce qui concerne les if() else() for & autres mais la j'ai du mal ;-)

Merci !
malalam Messages postés 10839 Date d'inscription lundi 24 février 2003 Statut Membre Dernière intervention 2 mars 2010 25
21 nov. 2006 à 04:37
'tite insomnie... ;-)
Un itérateur, c'est...heu...une méthode permettant d'accéder aux éléments d'une collection l'un après l'autre. Un itérateur n'utilise de ressource que pour l'élément en cours. Bref pour parcourir 1,2,3, ben via un itérateur, tu vas faire un rewind pour être au début, puis un next et tu ne connaitras toujours que l'élément en cours (1, puis 2, puis 3). Tu traverses la collection (traversablité est un maître mot pour les itérateurs), récursivement si tu utilises un itérateur récursif, sans utiliser d'autre ressource que celle utilisée pour connaître l'élément en cours.
Tu déclares ton itérateur sur un répertoire par exemple, en récursif, et il te suffit d'un foreach () sur ton itérateur pour lire TOUTE l'arborescence. Parce que le foreach () va aller chercher les méthodes de l'itérateur permettant de le traverser : rewind (), next (), valid (), key (), current (), et si récursivité, hasChild (), getChild (). Ca, c'est l'exemple basique hein...pour les interfaces Iterator et RecursiveIterator. Mais il y a des itérateurs implémentant ces interfaces qui permettent bien plus.
La SPL définit des itérateurs pour un peu tout, et surtout, des méthodes et interfaces très complètes pour chacun de ces itérateurs.
Je donnerai d'autres exemples, notamment le très sympa IteratorAggregate.
Ici, par exemple, le coup du "un par un" m'a pas mal embêté pour la méthode oSmartDir::copy () :
je crée mon itérateur sur le répertoire courant pour le copier dans mon nouveau répertoire, puis je crée le répertoire destination. Oui mais comme je copie en traversant mon itérateur...je finis par tomber sur le répertoire que j'ai créé, et dans lequel j'ai copié les éléments précédents! Donc, je fais next (), hasChild () => true ? getChild () ...etc et je copie...récursivement, et dans une boucle infinie. Puisqu'au fur et à mesure que je copie, ben je parcours... ;-)
C'est pourquoi j'ai dû ajouter un filtre privé sortant de la méthode oSmartDir::valid() en false si j'étais sur le répertoire destination. Histoire de ne pas rentrer dedans et le traverser.
TheSin Messages postés 331 Date d'inscription mardi 12 novembre 2002 Statut Membre Dernière intervention 10 février 2009
23 nov. 2006 à 07:28
malalam, si je comprend bien ton explication, il ne faut pas confondre itérateur et itération ?

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.