IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Fonction récursive pour remplissage Array


Sujet :

Langage PHP

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut Fonction récursive pour remplissage Array
    Bonjour à tous, j'espère tout d'abord poster au bon endroit ;

    J'ai bien utilisé la fonction de recherche avant, et trouvé une question presque similaire à la mienne, mais le résultat ne me convenait pas (requête dans une boucle, berk.)

    Voilà mon soucis :

    J'extrais d'une base les informations sur des forums (ID, titre, etc.).
    Chaque forum possède un champ 'parent_forum_id', qui peut avoir une valeur NULL. Si NULL, alors le forum est en première ligne, sans parent. Sinon, c'est le sous-forum du parent précisé.

    Du coup, je me retrouve avec un résultat ligne par ligne de forums / sous-forums, et je dois refaire l'arbre ; le but est de refaire cet arbre sous forme d'Array, qui pourra être exploité ultérieurement..



    Voici mon script à l'heure actuelle, qui permet d'avoir dans l'array :

    Les catégories et leurs infos, plus un champ 'children', qui contient NULL par défaut, puis qui contient les array des forums qu'il contient, avec leurs infos, plus un champ 'children', etc.

    J'arrive à créer l'Array avec les catégories, puis à y insérer les Forums sans parents, puis à insérer par le biais d'une fonction les premiers sous-forums ; mais impossible, pour mon pauvre esprit, de concevoir la fonction en mode récursif pour continuer dans la "généalogie"..

    Je comprends bien le concept d'une fonction récursive, mais juste, mes syntaxes finissent par planter (des erreurs d'index inexistants, ou encore de mémoire dépassée)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    <?php
    require('conf/conf.inc.php');
    $categories = $c->get("SELECT * FROM categories");
    $for_parents = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NULL");
    $for_children = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NOT NULL");
     
    $site = array();
    $enfants = array();
     
    foreach($categories as $cat){	
    	$site[$cat['cat_id']] = $cat;
    	$site[$cat['cat_id']]['children'] = NULL;
    }
    foreach($for_parents as $forum){
    	$site[$forum['cat_id']]['children'][$forum['forum_id']] = $forum;
    	$site[$forum['cat_id']]['children'][$forum['forum_id']]['children'] = NULL;
    }
    foreach($for_children as $forum){
    	$enfants[$forum['forum_id']] = $forum;
    }
     
    function addEnfants($parents, $enfants){
    	foreach($parents as $key=>&$parent){
    		foreach($enfants as &$enf){
    			if($enf['parent_forum_id'] == $key){
    				$parent['children'][$enf['forum_id']] = $enf;
    				$parent['children'][$enf['forum_id']]['children'] = NULL;
    				// ICI, je suis censé placer un "unset" pour vider petit à petit l'array des enfants, puis relancer la fonction "addEnfant()". C'est à cette ligne que je bloque...
    			}
    		}
    	}
    	return $parents;
    }
     
    foreach($site as &$parents){
    	$parents = addEnfants($parents['children'], $for_children);
    }
    unset($parents);
     
    echo '<pre>';
    var_dump($site);
    echo '</pre>';
    echo '<br />--------------<br />';
    ?>

    Si quelqu'un a une idée pour me décoincer Merci !

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Je post la suite de mon problème, ayant avancé seul..

    J'ai réussi à créer ma fonction, et le résultat est quasiement bon ; le seul bémol, c'est qu'au résultat final, mon array est correctemetn construit mais j'ai perdu au passage toute information concernant les catégories et les 1ers forums.

    Les sous-forums, quant à eux, sont parfaitement en place avec toutes leurs informations (titre, etc..)

    voilà mon script à l'heure actuelle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    <?php
    require('conf/conf.inc.php');
    $categories = $c->get("SELECT * FROM categories");
    $for_parents = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NULL");
    $for_children = $c->get("SELECT * FROM forums WHERE parent_forum_id IS NOT NULL");
     
    $site = array();
    $enfants = array();
     
    function addEnfants($parents, $enfants){
    	foreach($parents as $key=>&$parent){
    		foreach($enfants as &$enf){
    			if($enf['parent_forum_id'] == $parent['forum_id']){
    				$parent['children'][$enf['forum_id']] = $enf;
    				$parent['children'][$enf['forum_id']]['children'] = NULL;
    				$parent['children'] = addEnfants($parent['children'], $enfants);
    			}
     
    		}
    	}
    	return $parents;
    }
     
    // Initialisation des catégories
    foreach($categories as $cat){
    	$site[$cat['cat_id']] = $cat;
    	$site[$cat['cat_id']]['children'] = NULL;
    }
    // Initialisation des forums sans parents
    foreach($for_parents as $forum){
    	$site[$forum['cat_id']]['children'][$forum['forum_id']] = $forum;
    	$site[$forum['cat_id']]['children'][$forum['forum_id']]['children'] = NULL;
    }
    // On récupère tous les sous-forums
    foreach($for_children as $forum){
    	$enfants[$forum['forum_id']] = $forum;
    }
    // On créé le tableau contenant l'arbre
    foreach($site as &$parents){
    	$parents = addEnfants($parents['children'], $for_children);
    }
    unset($parents);
     
    echo '<pre>';
    var_dump($site);
    echo '</pre>';
    echo '<br />--------------<br />';
    ?>

  3. #3
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Salut WibiMaster,

    le seul bémol, c'est qu'au résultat final, mon array est correctemetn construit mais j'ai perdu au passage toute information concernant les catégories et les 1ers forums.
    C'est normal car ta fonction addEnfant devrait travailler avec des références et non des copies de tableaux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function addEnfant(&$parents, &$enfants)
    Enfin si je ne suis pas à côté de la plaque.

    De mon côté j'ai eu à faire un truc de ce genre et je te l'ai adapté (grosso modo) à tes besoins j'y ai mis des annotations dans le code histoire de ne pas être paumé. Je ne saurais que trop te conseiller de te pencher sur la SPL (Standard PHP Library) car elle offre des tas de fonctions sacrément utiles par exemple dans le cas de la récursivité. Et pour ne rien gâcher la récursivité est mise à plat et du coup tu ne risque pas de mettre à genoux le serveur en cas d'algorithme attaquant des données très volumineuses (vécu).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <?php
     
    require('conf/conf.inc.php');
     
    $categs = $c->get("SELECT * FROM categories");
    # il faut absolument avoir les forums parents avant les enfants
    $forums = $c->get("SELECT * FROM forums ORDER BY parent_forum_id");
     
    $map   = array();
    $paths = array();
     
    # récupération de toutes les catégories
    # pour profiter de la SPL (voir plus bas) il fallait éviter
    # les collisions entre les clés des cat_id et des forum_id
    # donc j'ai préfixé les cat_id avec un 'g'
    foreach($categs as $cat) {
       $map['g' . $cat['cat_id']] = array('type'      => 'categ',
                                          'details'    => $cat, 
                                          'subforums'  => array());
    }
     
    # arborescence des forums
    foreach($forums as $forum) {
     
       $idParent = $forum['parent_forum_id'];
       $idForum  = $forum['forum_id'];
     
       if (is_null($idParent)) {
          # forum racine
          $map['g' . $forum['cat_id']]['subforums'] += array($idForum => array('type'      => 'forum',
                                                                               'details'   => $forum, 
                                                                               'subforums' => array()));
          # chemin vers le forum courant
          $paths[$idForum] = array('g' . $forum['cat_id']);
     
       } else {
          # sous-forum
          # chemin vers le forum courant
          $paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          foreach (new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value) 
          {
             # si la clé courante correspond à la clé recherchée
             # on ajoute le sous-forum au tableau $map
             if ($key == $idParent) {
                $nb = count($paths[$idForum]) - 1;
                for($i = 0; $i <= $nb; $i++) {
                   if ($i == 0) {
                      $last =& $map[$paths[$idForum][$i]];
                   } else {
                      $last =& $last['subforums'][$paths[$idForum][$i]];
                   }
                }
                $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
             }
          }
       }
       var_dump($map);
    }
    ?>

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Merci !

    Déjà pour l'histoire des références, il me semblait qu'en la déclarant avec le "&" dans le foreach, je pouvais l'utiliser sans après.. Mais je me trompe peut-être.

    J'ai donc testé ton script, ça fonctionne presque ; les sous-forums de sous-forums ne sont pas pris en compte, et j'obtiens assez régulièrement l'erreur :

    Warning: Cannot use a scalar value as an array in C:\Program Files (x86)\EasyPHP-5.3.3\www\live4dev\test2.php on line 53

    dans le var_dump..

  5. #5
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Oui j'ai fait une boulette dans la ligne 53
    remplace $last &= par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if ($i == 0) {
       $last =& $map[$paths[$idForum][$i]];
    } else {
       $last =& $last['subforums'][$paths[$idForum][$i]];
    }
    Dans tous les cas j'ai essayé avec ce genre de données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $categs = array(array('cat_id' => 0, 'nom' => 'PHP'), 
                    array('cat_id' => 1, 'nom' => 'JAVA'), 
                    array('cat_id' => 2, 'nom' => 'VBA'));
     
    $forums = array(array('forum_id' => 1, 'cat_id' => 0, 'nom' => 'Général'),
                    array('forum_id' => 2, 'parent_forum_id' => 1, 'nom' => 'Code'),
                    array('forum_id' => 3, 'parent_forum_id' => 1, 'nom' => 'Syntaxe'),
                    array('forum_id' => 4, 'parent_forum_id' => 1, 'nom' => 'Langage'),
                    array('forum_id' => 5, 'parent_forum_id' => 4, 'nom' => 'Débutant'),
                    array('forum_id' => 6, 'parent_forum_id' => 4, 'nom' => 'Confirmé'));
    Et j'ai bien toute l'arborescence

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Presque bon xD

    C'est la première fois que j'utilise les RecursiveIteratorIterator, alors je prends mon temps pour lire, comprendre, en utilisant la doc.

    En tout cas, c'est quasiement bon, mais ça plantait (serveur down) quand je lançais la page. Alors j'ai essayé avec tes données, moins importantes que les miennes, et ça plante aussi.

    En coupant le chargement de la page au bout de quelques secondes avant que tout pète, j'ai pu remarquer qu'en fait, la boucle ne s'arrêtait jamais ; je retrouve

    array(3) {
    ["g0"]=>

    donc l'origine, plusieurs fois. Comme si toute la page se relançait sans arrêt, donc forcément, tout plante. J'essaie de voir où le script devrait détecter la fin et s'arrêter, pour l'améliorer ; étrange que cela fonctionne correctement chez toi, j'ai pourtant repris point par point tout ce que tu m'as donné..

    Les lignes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $last &= ($i == 0) ? $map[$paths[$idForum][$i]]
                                      : $last['subforums'][$paths[$idForum][$i]];
    sont bien devenues

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if ($i == 0) {
    					$last =& $map[$paths[$idForum][$i]];
    				} else {
    					$last =& $last['subforums'][$paths[$idForum][$i]];
    				}
    Mais c'est depuis que le script ne s'arrête pas ; j'ai du faire une erreur ici, je cherche et je tiens au courant ^^

    Merci de ton aide !

    [edit]
    En réalité, j'ai bien observer le var_dump (meme arreté avant la fin) ; ce n'est pas que le code se répète indéfiniment (quoi que, sans doute tout de même), c'est assez étrange ce qu'il ressort...

    Tout d'abord, ça me ressort l'array des catégories ; ensuite, l'array des catégories avec le premier forum ; ensuite, la meme chose mais avec plus de forums ; et ainsi de suite.. Comme s'il faisait un var_dump au fur et à mesure, alors que celui-ci est séparé de la boucle.

    Après vérification, celui-ci était bien dans la boucle. Je l'ai donc ressorti, et du coup, ça plante avant de faire le var_dump, je ne vois donc même pas ce qu'il peut contenir. La boucle ne s'arrête réellement jamais.. Je ne comprends pas, dans ton script, c'est un foreach sur un tableau fini, il ne devrait donc pas tourner éternellement...

    Ca doit etre à ce niveau là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    foreach (new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value)

  7. #7
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Salut WibiMaster,

    Bon je t'explique pourquoi chez moi cela a marché : je l'ai exécuté en mode debug avec un point d'arrêt sur var_dump($map) et avec le débug de PHPED j'ai inspecté le contenu de la variable $map et vu qu'il me parraissait correct, je coupais le sifflet au script sans le terminer proprement (J'avais sorti le var_dump de la boucle foreach). C'est la raison pour laquelle je n'ai pas vu que le script bouclait sans fin. J'suis un âne quand même parfois

    Je pense le hic se situe dans le fait que l'on modifie le tableau $map tout en le parcourant récursivement et ça doit poser problème à la SPL.

    Je me penche dessus.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Test après test, j'ai cibler le probleme un peu plus bas ;

    La boucle ne devrait pas etre infini, le $nb dans l'exemple vaut 1 à chaque tour. donc le

    for($i = 0; $i < $nb [...]

    devrait s'arreter au bout d'un tour, ce qu'il n'a pas l'air de faire..

    Ca semble bloquer au niveau du

    $last =&

    le truc, c'est que je n'arrive pas à comprendre cette syntaxe, même après recherche

    La boucle entière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
             # si la clé courante correspond à la clé recherchée
             # on ajoute le sous-forum au tableau $map
             if ($key == $idParent) {
                $nb = count($paths[$idForum]) - 1;
    				//echo $nb.'--';
                for($i = 0; $i <= $nb; $i++) {
                   /*$last &= ($i == 0) ? $map[$paths[$idForum][$i]]
                                      : $last['subforums'][$paths[$idForum][$i]];*/
    				if ($i == 0) {
    					$last =& $map[$paths[$idForum][$i]];
    				} else {
    					$last =& $last['subforums'][$paths[$idForum][$i]];
    				}
                }
                $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
             }

  9. #9
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    $last =&

    le truc, c'est que je n'arrive pas à comprendre cette syntaxe, même après recherche
    Cela veut dire que la variable $last pointe vers la même zone mémoire que ce qui est défini à droite de l'opérande =& (en l'occurrence ici une partie du tableau $map), on travaille directement sur le tableau original sans le dupliquer.
    J'ai fait un essai sans rien modifer au script mais juste en rajoutant exit(); après le var_dump() et là tu as bien le rendu.

  10. #10
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    ça y est l'os n'est plus :-)

    Tu conserves tout le script tel quel et tu lui rajoute un minable après
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
    Je remet la partie corrigée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    # arborescence des forums
    foreach($forums as $forum) {
     
       $idParent = $forum['parent_forum_id'];
       $idForum  = $forum['forum_id'];
     
       if (is_null($idParent)) {
          # forum racine
          $map['g' . $forum['cat_id']]['subforums'] += array($idForum => array('type'      => 'forum',
                                                                               'details'   => $forum, 
                                                                               'subforums' => array()));
          $paths[$idForum] = array('g' . $forum['cat_id']);
     
       } else {
          # sous-forum
          # on sauveagrde le chemin vers le forum courant
          $paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          foreach(new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value) 
          {
             # si la clé courante correspond à la clé recherchée
             # on ajoute le sous-forum au tableau $map
             if ($key == $idParent) {
                $nb = count($paths[$idForum]) - 1;
                for($i = 0; $i <= $nb; $i++) {
                   if ($i == 0) {
                      $last =& $map[$paths[$idForum][$i]];
                   } else {
                      $last =& $last['subforums'][$paths[$idForum][$i]];
                   }
                }
                $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
                break;
             }
          }
       }
    }
     
    var_dump($map);
    Voili voilou,

    Bon dev

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut


    Rien à redire... Respect.

    Et surtout un énorme merci parce qu'en plus tu post les explications avec, et ça c'est génial pour apprendre de ses erreurs

    Bon, ça plante avec ma récupération de données, mais ça doit venir des arrays qui ressortent, je reglerais ça rapidement vu que ça marche avec les tiens qui semblent similaires.

    Je marque résolu, et je t'en dois une belle !

    Je te copyrighterais à l'occasion si dans mon entourage je vois un problème similaire :p

    Encore merci !!!

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Et pour ne rien gâcher la récursivité est mise à plat et du coup tu ne risque pas de mettre à genoux le serveur en cas d'algorithme attaquant des données très volumineuses (vécu).
    Je n'ai que 19 lignes dans ma table, et je dépasse déjà la mémoire allouée au lancement du script

    [EDIT]
    Après vérification, le foreach sur le new RecursiveIteratorIterator fais quelques centaines de tours avant de planter, alors que je n'ai que 19 lignes... Il y a donc bien un problème, autre que le break que l'on a placé..

  13. #13
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Salut,

    Tu peux aussi écrire le foreach comme ça et fais un essai avec tes données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    49
    # arborescence des forums
    foreach($forums as $forum) {
     
       $idParent = $forum['parent_forum_id'];
       $idForum  = $forum['forum_id'];
     
       if (is_null($idParent)) {
          # forum racine
          $map['g' . $forum['cat_id']]['subforums'] += array($idForum => array('type'      => 'forum',
                                                                               'details'   => $forum, 
                                                                               'subforums' => array()));
          $paths[$idForum] = array('g' . $forum['cat_id']);
     
       } else {
          # sous-forum
          # on sauveagrde le chemin vers le forum courant
          $paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          $arIter = new RecursiveArrayIterator($map);
          $itIter = new RecursiveIteratorIterator($arIter, RecursiveIteratorIterator::SELF_FIRST);
          /*
          foreach(new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value) 
          
          */
          foreach($itIter as $key => $value)
          {
             # si la clé courante correspond à la clé recherchée
             # on ajoute le sous-forum au tableau $map
             if ($key == $idParent) {
                $nb = count($paths[$idForum]) - 1;
                for($i = 0; $i <= $nb; $i++) {
                   if ($i == 0) {
                      $last =& $map[$paths[$idForum][$i]];
                   } else {
                      $last =& $last['subforums'][$paths[$idForum][$i]];
                   }
                }
                $last['subforums'] += array($idForum => array('type'      => 'forum',
                                                              'details'   => $forum,
                                                              'subforums' => array()));
                break;
             }
          }
       }
    }
     
    var_dump($map);

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Cela plante également...

    J'ai mis le break en dehors de la condition, et ça ne plante plus ; cependant, ça ne me prends pas en compte les sous forums.

    Ci-joint un aperçu de mes tables catégories et forums, donc qui ressortent comme ton exemple d'array... (avec un sous-forum en plus, je ne sais pas si ça change grand chose..)

    La boucle fait vraiment trop de tour, elle fais plus que boucler sur l'array uniquement



    [EDIT]
    J'ai fais un test en initialisant le nombre de tour à 0 avant le foreach problematique, et en faisant un echo de ce nombre incrémenter de 1 à chaque tour ; ça plante, quand j'en arrive à 169228.

    Je mets mon code dans son intégralité, sait-on jamais... (j'ai ré-ordonner les données sorties, le var_dump donne bien le résultat attendu)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
     
    <?php
    //echo memory_get_usage().'<br />';
    require('conf/conf.inc.php');
    $categories = $c->get("SELECT * FROM categories");
    # il faut absolument avoir les forums parents avant les enfants
    $forumies = $c->get("SELECT * FROM forums ORDER BY parent_forum_id");
     
    $categs = array();
    foreach($categories as $ca){
    	$categs[] = array(
    		'cat_id' => (int) $ca['cat_id'],
    		'cat_titre' => (string) $ca['cat_titre'],
    		'cat_description' => (string) $ca['cat_description']
    	);
    }
    $forums = array();
    foreach($forumies as $fo){
    	$forums[] = array(
    		'parent_forum_id' => ($fo['parent_forum_id'])?(int) $fo['parent_forum_id']:null,
    		'cat_id' => (int) $fo['cat_id'],
    		'forum_id' => (int) $fo['forum_id'],
    		'forum_titre' => (string) $fo['forum_titre'],
    		'forum_description' => (string) $fo['forum_description']
    	);
    }
    asort($forums);
     
    $map   = array();
    $paths = array();
    # récupération de toutes les catégories
    # pour profiter de la SPL (voir plus bas) il fallait éviter
    # les collisions entre les clés des cat_id et des forum_id
    # donc j'ai préfixé les cat_id avec un 'g'
    //echo memory_get_usage().'<br />';
    foreach($categs as $cat){
    	$map['g' . $cat['cat_id']] = array('type'       => 'categ',
    									   'details'    => $cat,
    									   'subforums'  => array());
    }
    //echo memory_get_usage().'<br />';
    # arborescence des forums
    foreach($forums as $forum){
    	$idParent = $forum['parent_forum_id'];
    	$idForum  = $forum['forum_id'];
    	if(is_null($idParent)){
    		# forum racine
    		$map['g' . $forum['cat_id']]['subforums'] += array($idForum => array('type'      => 'forum',
    																			 'details'   => $forum, 
    																			 'subforums' => array()));
    		# chemin vers le forum courant
    		$paths[$idForum] = array('g' . $forum['cat_id']);
       }else{
    		# sous-forum
    		# chemin vers le forum courant
    		$paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          $arIter = new RecursiveArrayIterator($map);
          $itIter = new RecursiveIteratorIterator($arIter, RecursiveIteratorIterator::SELF_FIRST);
          /*
          foreach(new RecursiveIteratorIterator(
                      new RecursiveArrayIterator($map),
                            RecursiveIteratorIterator::SELF_FIRST) as $key => $value){
          
          */
    	  $tour = 0;
          foreach($itIter as $key => $value)
          {
    			$tour++;
    			echo $tour.'<br />';
    			# si la clé courante correspond à la clé recherchée
    			# on ajoute le sous-forum au tableau $map
    			//echo memory_get_usage().'<br />';
    			if($key == $idParent){
    				$nb = count($paths[$idForum]) - 1;
    				for($i = 0; $i <= $nb; $i++){
    					if($i == 0){
    						$last =& $map[$paths[$idForum][$i]];
    					}else{
    						$last =& $last['subforums'][$paths[$idForum][$i]];
    					}
    				}
    				$last['subforums'] += array($idForum => array('type'      => 'forum',
    															  'details'   => $forum,
    															  'subforums' => array()));
    				break;
    			}
     
    		}
    	}
    }
    echo '<pre>';
    var_dump($map);
    echo '</pre>';
    ?>

  15. #15
    Expert confirmé
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Billets dans le blog
    12
    Par défaut
    Décidemment il y a des jours où il faudrait mieux rester couché.

    Bon comme je suis un sale entêté teigneux, j'ai continué à creuser et voici ce que je te propose. J'ai quand même un peu honte car c'est désarmant de simplicité.
    Je t'y ai mis aussi un tableau de données qui me faisait le même dépassement de mémoire que toi avec le RecursiveIteratorIterator/RecursiveArrayIterator.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    <?php
     
    $categs = array(array('cat_id' => 0, 'nom' => 'PHP'), 
                    array('cat_id' => 1, 'nom' => 'JAVA'), 
                    array('cat_id' => 2, 'nom' => 'VBA'));
     
    $forums = array(array('forum_id' => 1, 'cat_id' => 0, 'nom' => 'Général'),
                    array('forum_id' => 2, 'parent_forum_id' => 1, 'nom' => 'Code'),
                    array('forum_id' => 3, 'parent_forum_id' => 1, 'nom' => 'Syntaxe'),
                    array('forum_id' => 4, 'parent_forum_id' => 1, 'nom' => 'Langage'),
                    array('forum_id' => 5, 'parent_forum_id' => 4, 'nom' => 'Débutant'),
                    array('forum_id' => 6, 'parent_forum_id' => 4, 'nom' => 'Confirmé'),
                    array('forum_id' => 7, 'cat_id' => 2, 'nom' => 'Général'),
                    array('forum_id' => 8, 'parent_forum_id' => 7, 'nom' => 'Code'),
                    array('forum_id' => 9, 'parent_forum_id' => 7, 'nom' => 'Syntaxe'),
                    array('forum_id' => 10, 'parent_forum_id' => 7, 'nom' => 'Langage'),
                    array('forum_id' => 11, 'parent_forum_id' => 10, 'nom' => 'Débutant'),
                    array('forum_id' => 12, 'parent_forum_id' => 10, 'nom' => 'Confirmé'),
                    array('forum_id' => 13, 'cat_id' => 1, 'nom' => 'Général'),
                    array('forum_id' => 14, 'parent_forum_id' => 13, 'nom' => 'Code'),
                    array('forum_id' => 15, 'parent_forum_id' => 13, 'nom' => 'Syntaxe'),
                    array('forum_id' => 16, 'parent_forum_id' => 13, 'nom' => 'Langage'),
                    array('forum_id' => 17, 'parent_forum_id' => 16, 'nom' => 'Débutant'),
                    array('forum_id' => 18, 'parent_forum_id' => 16, 'nom' => 'Confirmé'));
     
    $map   = array();
    $paths = array();
     
    # récupération de toutes les catégories il fallait éviter
    # les collisions entre les clés des cat_id et des fourm_id
    # donc j'ai préfixé les cat_id avec un 'g'
    foreach($categs as $cat) {
       $map['g' . $cat['cat_id']] = array('type' => 'categ',
                                          'details'    => $cat, 
                                          'subforums'  => array());
    }
     
    # arborescence des forums
    foreach($forums as $forum) {
     
       $idParent = $forum['parent_forum_id'];
       $idForum  = $forum['forum_id'];
     
       $current = array($idForum => array('type'      => 'forum',
                                          'details'   => $forum, 
                                          'subforums' => array()));
     
       if (is_null($idParent)) {
          # forum racine
          $map['g' . $forum['cat_id']]['subforums'] += $current;
          $paths[$idForum] = array('g' . $forum['cat_id']);
     
       } else {
          # sous-forum
          # on sauvegarde le chemin vers le forum courant
          $paths[$idForum] = array_merge($paths[$idParent], array($idParent));
     
          $nb = count($paths[$idForum]);
          for($i = 0; $i < $nb; $i++) {
             if ($i == 0) {
                $last =& $map[$paths[$idForum][$i]];
             } else {
                $last =& $last['subforums'][$paths[$idForum][$i]];
             }
          }
          $last['subforums'] += $current;
       }
    }
     
    var_dump($map);
     
    ?>
    Et là tu vas me dire comment n'y ai-je pas pensé plutôt et bien saches que je n'en sais rien, si c'est bon tu peux me noter et si cela foire tu pourras me fouetter, promis
    Je me suis absenté 2 heures et au retour ça m'a fait splash boum la-dedans.

    De mon côté je n'arrive pas à expliquer pourquoi un banal RecursiveIteratorIterator/RecursiveArrayIterator fait un dépassement de capacité mémoire. Je vais chercher et si je trouve une explication qui tienne la route je ferais un suivi ici

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 56
    Par défaut
    Bon comme je suis un sale entêté teigneux, j'ai continué à creuser et voici ce que je te propose.
    J'admire ta ténacité fasse à un problème qui ne te concerne pas au premier abord

    J'ai quand même un peu honte car c'est désarmant de simplicité.
    Bah oui et non, vu le soucis je me doutais qu'une résolution extrêmement simple existait, mais à côté de ça j'ai pas été foutu de la trouver

    Et là tu vas me dire comment n'y ai-je pas pensé plutôt et bien saches que je n'en sais rien, si c'est bon tu peux me noter et si cela foire tu pourras me fouetter, promis
    20/20

    De mon côté je n'arrive pas à expliquer pourquoi un banal RecursiveIteratorIterator/RecursiveArrayIterator fait un dépassement de capacité mémoire. Je vais chercher et si je trouve une explication qui tienne la route je ferais un suivi ici
    La réponse m'intéresse également, j'ai du mal à saisir le comportement exact de la syntaxe qu'on a utilisé. Pourtant, j'ai vu la même sur la doc. Au vue du résultat, ça donne l'impression qu'il boucle exponentiellement sur l'intégralité du tableau, c'est à dire que pour un array type

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    array(
        ['champ1'] => 'blabla',
        ['champ2'] => 'blabla',
        ['champ3'] => 'blabla',
        ['champ4'] => 'blabla',
        ['champ5'] => 'blabla'
    );
    Il va faire sa boucle dans ce style :

    1e tour : champ1
    2e tour : champ1, 3e tour : champ2
    4e tour : champ1, 5e tour : champ2, 6e tour : champ3

    Résultat 6 tours pour arriver au champ3.

    Ce n'est qu'une hypothèse, mais ça expliquerait pourquoi avec 19 lignes de 5 champs + 4 lignes de 3 champs il arrive à faire un nombre de tour supérieur à 160000...
    Peut-être la manière dont le script est agencer. Pourtant selon la doc, si j'ai bien compris, avec cette méthode il efface petit à petit ce sur quoi il a déjà bouclé (suis pas sûr de ça, mais y a une phrase dans la doc qui le fais présager, selon une traduction arbitraire de ma part ^^) donc le dépassement de mémoire ne devrait pas se produire...


    Quoi qu'il en soit, ta dernière réponse correspond exactement à ce que j'essayais de mettre en place dès le début ^^ Je n'ai juste pas pensé à l'astuce du $current, et à l'array_merge...

    S'il existe un vrai système de notation dans ce forum, j'te mets la note maximale immédiatement pour ta détermination et le résultat obtenu !!

    Encore merci !!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fonction récursive pour créer un array
    Par laloune dans le forum Langage
    Réponses: 18
    Dernier message: 26/02/2013, 12h39
  2. [MySQL] Fonction récursive pour affichage arborescence
    Par Mister Paul dans le forum PHP & Base de données
    Réponses: 11
    Dernier message: 01/12/2007, 19h30
  3. Réponses: 6
    Dernier message: 12/04/2007, 20h30
  4. Réponses: 10
    Dernier message: 03/07/2006, 11h32
  5. Fonctions récursives pour parcourir un arbre
    Par mikedavem dans le forum C
    Réponses: 4
    Dernier message: 05/06/2006, 12h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo