Précédent   Forum du club des développeurs et IT Pro > PHP > Langage
Langage Forum sur le langage PHP, la POO, les conventions, la sécurité, etc. Avant de poster : FAQ Langage, toutes les FAQ PHP, cours langage et sources PHP
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Actualité déjà publiée
 
Outils de la discussion
Publicité
'
Vieux 04/09/2012, 22h05   #1
tarikbenmerar
Chroniqueur Actualités
 
Homme Tarik Zakaria Benmerar
CEO
Inscription : juillet 2012
Messages : 78
Détails du profil
Informations personnelles :
Nom : Homme Tarik Zakaria Benmerar
Localisation : Algérie

Informations professionnelles :
Activité : CEO
Secteur : High Tech - Multimédia et Internet

Informations forums :
Inscription : juillet 2012
Messages : 78
Points : 1 361
Points : 1 361
Par défaut PHP introduit les générateurs, par un mécanisme similaire à celui de Python

PHP introduit les générateurs
Par un mécanisme similaire à celui de Python avec le mot-clé yield


Les générateurs sont un moyen simple et puissant de créer des itérateurs dans des langages tels que Python. Maintenant, c'est PHP qui fait le pas et s'approprie ce concept.

Pour comprendre l'utilité et la puissance de ce dernier, on revoit l'exemple typique de lecture d'un fichier en entier :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
function getLinesFromFile($fileName) {
    if (!$fileHandle = fopen($fileName, 'r')) {
        return;
    }
 
    $lines = [];
    while (false !== $line = fgets($fileHandle)) {
        $lines[] = $line;
    }
 
    fclose($fileHandle);
 
    return $lines;
}
 
$lines = getLinesFromFile($fileName);
foreach ($lines as $line) {
    // do something with $line
}
Le point faible de ce code est le fait qu'il copie tout le fichier dans un grand tableau. Ainsi, plus le fichier est grand, plus le besoin en mémoire s'accroît, avec un risque imminent d'atteindre les limites. Il faut toujours se rappeler qu'un script PHP doit respecter une limite de mémoire spécifiée par l'administrateur.

On peut certainement éviter ce comportement et récupérer les données ligne par ligne, en utilisant les itérateurs qui sont parfaits pour ce cas d'utilisation. Malheureusement, en PHP il n'existait jusque-là aucune manière simple d'implémenter les itérateurs. Pour y arriver, on est amené à créer une classe complexe implémentant une interface Iterator comme suit :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 
class LineIterator implements Iterator {
    protected $fileHandle;
    protected $line;
    protected $i;
 
    public function __construct($fileName) {
        if (!$this->fileHandle = fopen($fileName, 'r')) {
            throw new RuntimeException('Couldn\'t open file "' . $fileName . '"');
        }
    }
 
    public function rewind() {
        fseek($this->fileHandle, 0);
        $this->line = fgets($this->fileHandle);
        $this->i = 0;
    }
 
    public function valid() {
        return false !== $this->line;
    }
 
    public function current() {
        return $this->line;
    }
 
    public function key() {
        return $this->i;
    }
 
    public function next() {
        if (false !== $this->line) {
            $this->line = fgets($this->fileHandle);
            $this->i++;
        }
    }
    public function __destruct() {
        fclose($this->fileHandle);
    }
}
 
$lines = new LineIterator($fileName);
foreach ($lines as $line) {
    // do something with $line
}
Pour un cas aussi simple que notre exemple, le code est déjà complexe. Avec les générateurs, on peut considérablement réduire le nombre de lignes de code de manière directe à partir de la première version du code :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
function getLinesFromFile($fileName) {
    if (!$fileHandle = fopen($fileName, 'r')) {
        return;
    }
 
    while (false !== $line = fgets($fileHandle)) {
        //c'est ici la différence
        yield $line;
    }
 
    fclose($fileHandle);
}
 
$lines = getLinesFromFile($fileName);
foreach ($lines as $line) {
    // do something with $line
}
La toute petite différence réside dans l'utilisation du mot clé yield, qui génère une nouvelle donnée dans l'itération.

En effet, l'instruction $lines = getLinesFromFile($fileName) ne renvoie aucune donnée, c'est simplement un générateur qui implémente l'itérateur qui vient d'être créé.

Après, pendant l'exécution de la boucle foreach ($lines as $line), chaque itération génère les données renvoyées par yield, qui seront stockées dans $line. En fait, cette génération implémente un objet Iterator et des appels à Iterator::next() seront effectués. L'exécution s'arrête jusqu'à la rencontre du prochain yield, qui renvoie la prochaine donnée, et ainsi de suite...

Quelques jours après l'introduction du mot clé Finally, se succèdent donc pour PHP les bonnes nouvelles. Ou les emprunts d’autres langages, diront certains.



Source : détails du mot clé yield dans le site de PHP

Et vous ?

Quelle impression vous laisse cette annonce ?
Pouvez-vous trouver d'autres cas d'utilisation intéressants ?
Quelle autre approche de simplification des itérateurs auriez-vous préférée pour PHP ?
tarikbenmerar est déconnecté   Envoyer un message privé Réponse avec citation 70
Vieux 05/09/2012, 00h57   #2
spidermario
Membre émérite
 
Étudiant
Inscription : septembre 2006
Messages : 510
Détails du profil
Informations personnelles :
Âge : 19

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : septembre 2006
Messages : 510
Points : 905
Points : 905
Citation:
Envoyé par tarikbenmerar Voir le message
Quelle autre approche de simplification des itérateurs auriez-vous préférée pour PHP ?
En fait, pour l’exemple donné, une autre était déjà possible :
Code php :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function forEachLineFromFile($fileName, $f) {
    if (!$fileHandle = fopen($fileName, 'r')) {
        return;
    }
 
    while (false !== $line = fgets($fileHandle)) {
        $f($line);
    }
 
    fclose($fileHandle);
}
 
forEachLineFromFile($filename, function($line) {
    // do something with $line
});

C’est proche de l’approche employée, par exemple, par Scheme et sa fonction call-with-input-file.
spidermario est déconnecté   Envoyer un message privé Réponse avec citation 30
Vieux 05/09/2012, 07h49   #3
Lupus Michaelis
Membre du Club
 
Homme Mickaël Wolff
Développeur informatique
Inscription : juin 2012
Messages : 11
Détails du profil
Informations personnelles :
Nom : Homme Mickaël Wolff
Localisation : France, Haut Rhin (Alsace)

Informations professionnelles :
Activité : Développeur informatique
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : juin 2012
Messages : 11
Points : 49
Points : 49
J'avais été enchanté de découvrir les générateurs avec Python. Mais c'est avec de la pratique qu'on peut vraiment juger de la pertinence d'une telle fonctionnalité. Donc je vais attendre de pester contre les bogues (et trous de sécurité) qu'aura introduit ce nouveau gadget.
Lupus Michaelis est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 08h32   #4
_skip
Expert Confirmé Sénior
 
Avatar de _skip
 
Homme
Développeur d'applications
Inscription : novembre 2005
Messages : 2 568
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 29
Localisation : Suisse

Informations professionnelles :
Activité : Développeur d'applications
Secteur : High Tech - Produits et services télécom et Internet

Informations forums :
Inscription : novembre 2005
Messages : 2 568
Points : 6 432
Points : 6 432
Ce n'est pas mal à voir comme ça.

Mais déléguer un code de lecture de fichiers à une autre méthode, ça impliquerait presque une gestion d'erreur par exception. Si fgets retourne false, ça ne veut pas dire que la fin du fichier a été atteinte, ça peut être une erreur IO.
_skip est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 10h29   #5
Grimly_old
Membre éclairé
 
Inscription : mars 2008
Messages : 284
Détails du profil
Informations forums :
Inscription : mars 2008
Messages : 284
Points : 322
Points : 322
Ce mot clé est un raccourci pour cette structure :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
public class Resultat extends Exception {
    var $resultat;
    public function __constructor($resultat) {
        $this->resultat = $resultat;
    }
 
    public function getResultat() {
        return $this->resultat;
    }
}
 
function monIterateur() {
     $resultat = calculs();
     throw new Resultat($resultat);
}
 
function utilisation() {
     do {
         try {
               monIterateur();
         } catch (Resultat $r) {
               $line = $r.getResultat();
               $continue = $line === FALSE; //On suppose que nos calculs nous retournent FALSE à la fin de lecture.
               if ($continue !== FALSE) {
                    doStuff($line);
               }
         }
     } while ($continue);
 
}
C'est un mécanisme identique qui se limite à un seul niveau de profondeur alors que dans mon exemple j'aurais pu utiliser autant d'intermédiaire que je souhaite entre "monIterateur" et "utilisation"

Je trouve donc ce mot clé comme un sucre syntaxique inutile.
Grimly_old est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 10h38   #6
transgohan
Expert Confirmé
 
Avatar de transgohan
 
Homme Baptiste ROUSSEL
Développeur Temps réel Embarqué
Inscription : janvier 2011
Messages : 1 299
Détails du profil
Informations personnelles :
Nom : Homme Baptiste ROUSSEL
Localisation : France, Territoire de Belfort (Franche Comté)

Informations professionnelles :
Activité : Développeur Temps réel Embarqué

Informations forums :
Inscription : janvier 2011
Messages : 1 299
Points : 2 889
Points : 2 889
Je trouve qu'avec l'exemple donnée on perd en lisibilité et compréhension.
Aucun retour de fonction mais il y en a bel et bien un de par l'implication de yield.
Ils viennent de créer un second mot clé de retour de fonction mais que je ne trouve pas très pratique.

J'ai parcouru la rfc et j'ai trouvé d'autres exemples qui me chagrinent comme celui là :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function echoLogger() {
    while (true) {
        echo 'Log: ' . yield . "\n";
    }
}
 
function fileLogger($fileName) {
    $fileHandle = fopen($fileName, 'a');
    while (true) {
        fwrite($fileHandle, yield . "\n");
    }
}
 
$logger = echoLogger();
// or
$logger = fileLogger(__DIR__ . '/log');
 
$logger->send('Foo');
$logger->send('Bar');
En gros on balance des objets en exécution parallèle avec ce yield et c'est loin d'être un comportement/code qu'on a l'habitude d'utiliser avec ce langage.

Voilà pour mes deux sous de réflexions.
__________________
Toujours se souvenir que la majorité des ennuis viennent de l'espace occupé entre la chaise et l'écran de l'ordinateur.
transgohan est déconnecté   Envoyer un message privé Réponse avec citation 40
Vieux 05/09/2012, 11h23   #7
laerne
Membre confirmé
 
Homme
Étudiant
Inscription : octobre 2011
Messages : 68
Détails du profil
Informations personnelles :
Sexe : Homme

Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : octobre 2011
Messages : 68
Points : 215
Points : 215
En bref, c'est du lazy-evaluation ? Mais limité pour un itérateur ?
laerne est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 11h24   #8
yohannc
Membre confirmé
 
Homme
Développeur Java
Inscription : octobre 2008
Messages : 100
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 26
Localisation : France, Morbihan (Bretagne)

Informations professionnelles :
Activité : Développeur Java
Secteur : Administration - Collectivité locale

Informations forums :
Inscription : octobre 2008
Messages : 100
Points : 202
Points : 202
Citation:
En gros on balance des objets en exécution parallèle avec ce yield et c'est loin d'être un comportement/code qu'on a l'habitude d'utiliser avec ce langage.
Je ne suis pas expert sur le sujet, mais cela repose plutôt sur un système de pointeurs que d'exécution parallèles, et cela semble être l'implémentation classique du mot clé Yield.
yohannc est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 12h35   #9
camus3
Membre émérite
 
Inscription : juillet 2010
Messages : 604
Détails du profil
Informations forums :
Inscription : juillet 2010
Messages : 604
Points : 902
Points : 902
C'est bien mais on aimerai aussi que de vieux "bugs" soient corrigés , hein

http://phpsadness.com/

mais toute nouveauté est bonne à prendre.
camus3 est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 05/09/2012, 13h00   #10
_skip
Expert Confirmé Sénior
 
Avatar de _skip
 
Homme
Développeur d'applications
Inscription : novembre 2005
Messages : 2 568
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 29
Localisation : Suisse

Informations professionnelles :
Activité : Développeur d'applications
Secteur : High Tech - Produits et services télécom et Internet

Informations forums :
Inscription : novembre 2005
Messages : 2 568
Points : 6 432
Points : 6 432
Citation:
Envoyé par yohannc Voir le message
Je ne suis pas expert sur le sujet, mais cela repose plutôt sur un système de pointeurs que d'exécution parallèles, et cela semble être l'implémentation classique du mot clé Yield.
Oui, il faut imaginer que la boucle consommateur exécute elle-même la boucle fournisseur de façon transparente à chaque tour de boucle dans son propre thread. Si on le déroule on obtient surement un truc proche de ce qu'a posté Grimly.
_skip est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 14h43   #11
ePoX
Membre Expert
 
Inscription : juillet 2004
Messages : 1 028
Détails du profil
Informations forums :
Inscription : juillet 2004
Messages : 1 028
Points : 1 040
Points : 1 040
Citation:
Envoyé par _skip Voir le message
Mais déléguer un code de lecture de fichiers à une autre méthode, ça impliquerait presque une gestion d'erreur par exception. Si fgets retourne false, ça ne veut pas dire que la fin du fichier a été atteinte, ça peut être une erreur IO.

C'est du php. Le fichier toujours est présent, disponible en lecture, et le disque dur n'à jamais de défaillances.
Sinon, c'est qu'il y à un problème qui mérite un déboggage manuel.

Sans troll aucun, php est un langage et une communauté optimiste.

mes 2 cents

PS: Je vous vois venir les aficionados de l'exception ect. Ce n'est pas la peine de me rétorquer on peut faire ceci-cela, car oui vous aurez probablement raison, mais non ce n'est pas la philosophie de ce langage simple, syntaxiquement épuré et simplement fonctionnel.
ePoX est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 15h46   #12
_skip
Expert Confirmé Sénior
 
Avatar de _skip
 
Homme
Développeur d'applications
Inscription : novembre 2005
Messages : 2 568
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 29
Localisation : Suisse

Informations professionnelles :
Activité : Développeur d'applications
Secteur : High Tech - Produits et services télécom et Internet

Informations forums :
Inscription : novembre 2005
Messages : 2 568
Points : 6 432
Points : 6 432
Citation:
Envoyé par ePoX Voir le message
C'est du php. Le fichier toujours est présent, disponible en lecture, et le disque dur n'à jamais de défaillances.
Sinon, c'est qu'il y à un problème qui mérite un déboggage manuel.

Sans troll aucun, php est un langage et une communauté optimiste.
Evidemment un script qui s'interrompt et le client qui bouffe un 500 c'est pas grave en soi, on le signale à l'administrateur, tu fais des tests et hop.
Ce qui l'est déjà plus ce sont les applications qui continuent leur traitement en pensant être dans un état normal puis à la fin on remarque un beau jour qu'on a perdu des données. Et cet exemple fourni ci-dessus est typiquement un bon silent fail.
_skip est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/09/2012, 19h21   #13
pcdwarf
Membre actif
 
Inscription : février 2010
Messages : 71
Détails du profil
Informations forums :
Inscription : février 2010
Messages : 71
Points : 195
Points : 195
Je m'étonne du code impératif donné en exemple avec pour argument principal que ça fait charger tout le fichier en ram et que c'est moche.
En effet Il est inutile et même néfaste de charger tout le fichier en ram si on peut le traiter ligne à ligne.

Sauf qu'il n'y a pas besoin d'usine à gaz pour traiter un fichier en une seule passe.

L'exemple deviens

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
 
function dosomethingwhithFile($fileName, $linefunction) {
    if (!$fileHandle = fopen($fileName, 'r')) {
        return false;
    }
 
    while (false !== $line = fgets($fileHandle)) {
        $linefunction($line);
    }
 
    fclose($fileHandle);
    return true;
}
La mémoire libérée sera bien mieux utilisée par le système d'exploitation qui est sensé déjà gérer les problématiques de cache/readahead donc ça ne devrait pas être moins performant qu'en lisant tout le fichier d'un coup.

Au cas ou ça ne serait pas clair, je ne critique pas le nouveau "yield" mais simplement que le problème soulevé dans l'exemple et prétendument résolu par ce nouvel élément relève largement plus d'une mauvaise approche que d'une limitation du langage.
pcdwarf est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 05/09/2012, 19h41   #14
Matthieu Vergne
Membre actif
 
Homme Matthieu Vergne
Doctorant (Requirements Engineering)
Inscription : novembre 2011
Messages : 130
Détails du profil
Informations personnelles :
Nom : Homme Matthieu Vergne
Localisation : France

Informations professionnelles :
Activité : Doctorant (Requirements Engineering)
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : novembre 2011
Messages : 130
Points : 193
Points : 193
Même remarque que transgohan, j'aime pas car on perd en intuitivité. Pas de return, mais ça retourne quelque chose. De plus, on perd complètement la logique d'exécution :
- on appel la fonction
- elle s'exécute et retourne le produit final
- on utilise le produit final

Là, on a :
- on appel la fonction
- on récupère un truc qui n'a rien à voir avec le contenu de la fonction
- on utilise ce truc et là ça exécute le contenu de la fonction (alors qu'on n'appelle pas la fonction en question)

Ou sinon, la fonction est belle et bien exécutée pour générer l'itérateur avec toutes ses itérations, mais dans ce cas où est la différence avec tout stocker dans un tableau (on a quand même remplit la mémoire) ? Vu l'article, je ne pense pas que ce soit le cas. Mais du coup le code perd en clarté : on ajoute un mot clé qui remet en cause le paradigme de base, qui est d'avoir une "suite d'instructions exécutées les unes à la suite des autres".
Matthieu Vergne est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/09/2012, 11h17   #15
elderion
Membre du Club
 
Inscription : février 2005
Messages : 85
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 85
Points : 62
Points : 62
Par rapport a la syntaxe et sa lisibilité :
si je fais une analogie avec un autre langage qui utilise yield :
C# utilise bien "yield return" et pas juste "yield".
J'aurai trouvé plus commode que PHP s'inspire de la syntaxe C# :
de considérer yield comme un complément du return et pas comme un remplacant.
elderion est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 06/09/2012, 14h45   #16
SurferIX
Membre émérite
 
Avatar de SurferIX
 
Homme Olivier Pons
Ingénieur développement logiciels
Inscription : mars 2008
Messages : 370
Détails du profil
Informations personnelles :
Nom : Homme Olivier Pons
Âge : 39
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : High Tech - Multimédia et Internet

Informations forums :
Inscription : mars 2008
Messages : 370
Points : 831
Points : 831
Envoyer un message via MSN à SurferIX
C'est bien, au moins ce genre de choses fera réagir les développeurs Php, et peut être qu'ils vont essayer d'apprendre python.

Je développe exclusivement en Php (99 % de mon temps), surtout principalement parce qu'il est le plus utilisé au monde, donc je ne fais pas un troll "Python est meilleur que Php".

Python est très puissant, et j'aimerais ne faire que du Python, mais malheureusement pas assez de gens en font de manière professionnelle (même si Fabien Potencier, le créateur de Synfony, avoue qu'il s'est très largement inspiré du framework python Django qui est exceptionnellement puissant et qui montre par là même à quel point c'est dommage de préférer Synfony 2 à Django qui est toujours plus puissant, et qui a des années d'avance sur le reste (oui oui j'ai bien dit que je développais à 99 % en Php, mais il faut reconnaitre les qualités de certains outils (quand ils en ont (et même si je ne m'en sers pas )))).

Ci suivent quelques uns de mes articles, et peut être que des gens voudront ajouter une corde à leur arc voulant maîtriser Python en plus de Php :

Je dis cela dans le cadre de 'yield', parce que ceux qui connaissent déjà Python n'ont aucun mal avec cette possibilité de créer des itérateurs, ce qui ne semble pas être le cas pour les autres.
__________________
Il ne faut pas oublier que la politesse et le respect sont mutuels.

Mon framework Web haute performance :
SurferIX est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/09/2012, 15h42   #17
elderion
Membre du Club
 
Inscription : février 2005
Messages : 85
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 85
Points : 62
Points : 62
(ceci reste mon strict point de vue)

j'ai fait quelques pas vers Python et meme Ruby :
sans meme parler de puissance et tout ca
rien que la syntaxe du langage me rebute, ca ressemble a du "vieux code".
Avec des "def", des __INIT__, puis l'encapsulation optionnelle.
C'est pas pour troller, juste expliquer pourquoi je suis réfractaire a Python.

C'est sans doute une question d'affinité, car j'aime la syntaxe Java ou PHP avec des accolades et des points virgule, etc...
Sans ca j'aurais surement adopté Python ou Ruby.
elderion est déconnecté   Envoyer un message privé Réponse avec citation 20
Vieux 17/09/2012, 22h46   #18
CyberDenix
Candidat au titre de Membre du Club
 
Inscription : juin 2004
Messages : 16
Détails du profil
Informations personnelles :
Âge : 29

Informations forums :
Inscription : juin 2004
Messages : 16
Points : 12
Points : 12
Je vais faire mon captain obvious, mais...

Code php :
1
2
3
4
5
6
7
8
9
10
 
while (($line = fgets($fileHandle)) !== false) {
 
  // Il suffit de traiter la ligne directement ici
  // -> Consommation mémoire quasi constante
  // -> Pas besoin d'un yield avec post processing super lent
 
  doSomething($line);
 
}

Si c'est juste pour traiter le fichier ligne par ligne, les itérateurs ou le yield me semblent hors de propos.

Mais je loupe peut-être le sens caché de cette formidable avancée technologique ?




Edit : Grilled by spidermario et pcdwarf. J'me sens moins seul !
CyberDenix est déconnecté   Envoyer un message privé Réponse avec citation 10
Réponse Actualité déjà publiée
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 13h53.


 
 
 
 
Partenaires

Hébergement Web