Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[PHP] recursief door directories zoeken

Pagina: 1
Acties:

Onderwerpen


  • posttoast
  • Registratie: April 2000
  • Laatst online: 11:48
Ik heb een heel simpel hobby-CMSje gebouwd in PHP dat werkt met verschillende modules (bijvoorbeeld een module "gebruiker"). Elke module is een directory gevuld met een aantal PHP- en templatebestanden waarbij er één bestand modulesettings.xml aanwezig is. Nu wil ik graag dat het systeem zelf "ziet" welke modules er aanwezig zijn en deze netjes in een menu toont. Dat lukt nu op zich, maar ik vraag me af of het niet netter kan.

Op dit moment doe ik het als volgt:
PHP:
1
2
3
4
5
6
7
8
9
// Walk through module-directories and find setting-files
$modulesettingsFiles = array();
$it = new RecursiveDirectoryIterator("./__modules");
foreach (new RecursiveIteratorIterator($it) as $file){
  $pathParts = pathinfo($file);
  if ($pathParts['basename'] == 'modulesettings.xml'){
    $modulesettingsFiles[] = (string)$file;
  }
}

Ik kijk dus recursief in de directory "__modules" en zet vervolgens alle paden naar de verschillende modulesettings.xml bestanden in een array. Werkt prima, maar het voelt een beetje slordig om alle bestanden af te gaan en vervolgens per bestand te checken of het de goede is ($pathParts['basename'] == 'modulesettings.xml'). Daarom vroeg ik mij af: is er geen elegantere oplossing?

Voor de duidelijkheid: ik ben geen enorme PHP-pro, dus het zou goed kunnen dat er iets enorm voor de hand liggends is wat ik over het hoofd zie.

omniscale.nl


  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Je zou glob kunnen gebruiken, daarmee kun je naar een specifiek patroon zoeken. Ik vind je oplossing overigens niet zo lelijk hoor.

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 14:03

orf

In ons cms moet je een module "registreren". In de backend ga je naar een pagina toe waar alle modules uitgelezen worden. De modules die nog niet geregistreerd staan worden daar getoond (door simpelweg de map "modules" uit te lezen). De module kun je dan opslaan met een aantal configuratie waardes: titel, rechten, caching, etc. Het uitlezen hoeft dan alleen maar in de admin te gebeuren en niet per pageview.

Waarom sla je de settings sowieso niet op in de database?

  • frickY
  • Registratie: Juli 2001
  • Laatst online: 27-11 09:24
Hoe dan ook zal er iets of iemand door alle bestanden heen moeten om te checken of het is wat je zoekt. Of je dat nu met een iterator doet of met bijv. glob(). Welke methode je kiest is denk ik sterk persoonlijk.

Persoonlijk zou ik ook voor een eigen iterator kiezen. Maar is je $file geen instantie van SplFileObject?
$file->getBasename() is in dat geval een stuk netter dan een call naar pathinfo().

  • posttoast
  • Registratie: April 2000
  • Laatst online: 11:48
mcdronkz schreef op dinsdag 01 februari 2011 @ 22:03:
Je zou glob kunnen gebruiken, daarmee kun je naar een specifiek patroon zoeken. Ik vind je oplossing overigens niet zo lelijk hoor.
Ah ja, daar zou ik ook nog naar kunnen kijken. Dank :)
orf schreef op dinsdag 01 februari 2011 @ 22:03:
In ons cms moet je een module "registreren". In de backend ga je naar een pagina toe waar alle modules uitgelezen worden. De modules die nog niet geregistreerd staan worden daar getoond (door simpelweg de map "modules" uit te lezen). De module kun je dan opslaan met een aantal configuratie waardes: titel, rechten, caching, etc. Het uitlezen hoeft dan alleen maar in de admin te gebeuren en niet per pageview.

Waarom sla je de settings sowieso niet op in de database?
Het is wel een idee om alles in een database op te slaan, maar dan zal ik sowieso nog even door de directories moeten lopen om te kijken welke modules er zijn. Maar vanuit een database is inderdaad wel mooier denk ik, zeker omdat je het dan kunt koppelen aan gebruikers/rechten/etc. Maar goed, dat gaat op dit moment nogal ver voor mijn zeer eenvoudige CMS ;)
frickY schreef op dinsdag 01 februari 2011 @ 22:04:
Hoe dan ook zal er iets of iemand door alle bestanden heen moeten om te checken of het is wat je zoekt. Of je dat nu met een iterator doet of met bijv. glob(). Welke methode je kiest is denk ik sterk persoonlijk.

Persoonlijk zou ik ook voor een eigen iterator kiezen. Maar is je $file geen instantie van SplFileObject?
$file->getBasename() is in dat geval een stuk netter dan een call naar pathinfo().
Yes, je hebt gelijk. Dat was niet netjes. Ik heb het aangepast:
PHP:
1
2
3
4
5
6
7
8
// Walk through module-directories and find setting-files
$modulesettingsFiles = array();
$it = new RecursiveDirectoryIterator("./__modules");
foreach (new RecursiveIteratorIterator($it) as $file){
  if ($file->getBasename() == 'modulesettings.xml'){
    $modulesettingsFiles[] = (string)$file;
  }
}

omniscale.nl


  • kluyze
  • Registratie: Augustus 2004
  • Niet online
Waarom ga je elk bestand in de module map bekijken of dit het juiste bestand is? Als je alle module mappen afgaat en met iets als file_exists gaat kijken of de xml in de map bestaat dan heb je toch minder iteraties nodig.

Ik zie dat die ook wat raar doet als ik die hier test. Ik heb 2 mappen met elk 2 bestanden en een var_dump van $file dit krijg ik als output (de 'found it' echo zit in de if):
code:
1
2
3
4
5
6
7
object(SplFileInfo)#6 (2) { ["pathName":"SplFileInfo":private]=> string(33) "modules\.\test\modulesettings.xml" ["fileName":"SplFileInfo":private]=> string(18) "modulesettings.xml" } 
found it
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(24) "modules\.\test\test0.txt" ["fileName":"SplFileInfo":private]=> string(9) "test0.txt" } 
object(SplFileInfo)#6 (2) { ["pathName":"SplFileInfo":private]=> string(23) "modules\.\two\test1.txt" ["fileName":"SplFileInfo":private]=> string(9) "test1.txt" } 
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(23) "modules\.\two\test2.txt" ["fileName":"SplFileInfo":private]=> string(9) "test2.txt" } 
object(SplFileInfo)#5 (2) { ["pathName":"SplFileInfo":private]=> string(21) "modules\two\test1.txt" ["fileName":"SplFileInfo":private]=> string(9) "test1.txt" } 
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(21) "modules\two\test2.txt" ["fileName":"SplFileInfo":private]=> string(9) "test2.txt" }
4 bestanden maar wel 6 iteraties.
Structuur:
code:
1
2
3
4
5
6
7
modules
|- test
|  |- modulesettings.xml
|  |- test0.txt
|- two
   |- test1.txt
   |- test2.txt
Over de 2de map gaat hij om een of andere reden 2 keer.

Bij deze structuur zijn eigenlijk maar 2 iteraties nodig.

Verwijderd

Een andere mogelijkheid, die een wel compleet andere aanpak vergt, en wat refactoring, en mischien niet eens relevant is binnen je huidige code is het gebruik van wat ik het Scavenger (of harvester) Pattern. Drupal maakt gebruik van een soortgelijk pattern om bepaalde eigenschappen van modules op te vragen.

De aanpak varieert enigszins of je een OOP opzet hebt of een procedurele aanpak maar voor het voorbeeld lijkt me het niet relevant... dus ga ik maar uit van een OOP variant.

Bij deze aanpak itereer je ook door de files in je modules directory / of leest deze uit de database (lijkt me geen slecht idee om niet iedere keer door al je modules te itereren maar dit te cachen in de database) ... anyways ... Voor deze methode zou je dan bijvoorbeeld een method in iedere module class hebben, bijvoorbeeld getModuleSettings() ... deze zou dan een array terug kunnen geven met settings en deze tonen in een overzicht.

Zoals ik al zei ... dit vergt een compleet andere aanpak maar zou wel zorgen voor een meer 'self contained' module structuur.

  • posttoast
  • Registratie: April 2000
  • Laatst online: 11:48
kluyze schreef op dinsdag 01 februari 2011 @ 22:40:
Waarom ga je elk bestand in de module map bekijken of dit het juiste bestand is? Als je alle module mappen afgaat en met iets als file_exists gaat kijken of de xml in de map bestaat dan heb je toch minder iteraties nodig.
Is dat minder zwaar voor de server? Doet file_exists() niet gewoon hetzelfde wat ik doe eigenlijk? (geen retorische vraag, ik weet het echt niet)
Ik zie dat die ook wat raar doet als ik die hier test. Ik heb 2 mappen met elk 2 bestanden en een var_dump van $file dit krijg ik als output (de 'found it' echo zit in de if):
code:
1
2
3
4
5
6
7
object(SplFileInfo)#6 (2) { ["pathName":"SplFileInfo":private]=> string(33) "modules\.\test\modulesettings.xml" ["fileName":"SplFileInfo":private]=> string(18) "modulesettings.xml" } 
found it
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(24) "modules\.\test\test0.txt" ["fileName":"SplFileInfo":private]=> string(9) "test0.txt" } 
object(SplFileInfo)#6 (2) { ["pathName":"SplFileInfo":private]=> string(23) "modules\.\two\test1.txt" ["fileName":"SplFileInfo":private]=> string(9) "test1.txt" } 
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(23) "modules\.\two\test2.txt" ["fileName":"SplFileInfo":private]=> string(9) "test2.txt" } 
object(SplFileInfo)#5 (2) { ["pathName":"SplFileInfo":private]=> string(21) "modules\two\test1.txt" ["fileName":"SplFileInfo":private]=> string(9) "test1.txt" } 
object(SplFileInfo)#7 (2) { ["pathName":"SplFileInfo":private]=> string(21) "modules\two\test2.txt" ["fileName":"SplFileInfo":private]=> string(9) "test2.txt" }
4 bestanden maar wel 6 iteraties.
Structuur:
code:
1
2
3
4
5
6
7
modules
|- test
|  |- modulesettings.xml
|  |- test0.txt
|- two
   |- test1.txt
   |- test2.txt
Over de 2de map gaat hij om een of andere reden 2 keer.

Bij deze structuur zijn eigenlijk maar 2 iteraties nodig.
Kun je jouw PHP posten? Ik snap niet zo goed hoe je aan deze output komt.
Verwijderd schreef op dinsdag 01 februari 2011 @ 23:24:
Een andere mogelijkheid, die een wel compleet andere aanpak vergt, en wat refactoring, en mischien niet eens relevant is binnen je huidige code is het gebruik van wat ik het Scavenger (of harvester) Pattern. Drupal maakt gebruik van een soortgelijk pattern om bepaalde eigenschappen van modules op te vragen.

De aanpak varieert enigszins of je een OOP opzet hebt of een procedurele aanpak maar voor het voorbeeld lijkt me het niet relevant... dus ga ik maar uit van een OOP variant.

Bij deze aanpak itereer je ook door de files in je modules directory / of leest deze uit de database (lijkt me geen slecht idee om niet iedere keer door al je modules te itereren maar dit te cachen in de database) ... anyways ... Voor deze methode zou je dan bijvoorbeeld een method in iedere module class hebben, bijvoorbeeld getModuleSettings() ... deze zou dan een array terug kunnen geven met settings en deze tonen in een overzicht.

Zoals ik al zei ... dit vergt een compleet andere aanpak maar zou wel zorgen voor een meer 'self contained' module structuur.
Dat klinkt op zich heel erg goed en netjes (vooral dat cachen in de database, dat zet ik absoluut op de todo-lijst), maar wat maakt dit meer 'self contained' dan wat ik nu doe? Nu itereer ik ook door iedere directory en modulesettings.xml bevat alle informatie over die module. Aan de hand hiervan wordt het menu in het CMS gegenereerd en laadt een frontcontroller de juiste PHP-files (als iemand dus naar www.sitenaam.com/_admin/users/new gaat wordt de module users geladen).

Overigens bestaat het gros van mijn CMS uit ouderwetsche, procedurele code. Niet zo modern dus, maar ik heb wel geprobeerd alles zo netjes mogelijk op te zetten.

Meer kritiek is erg welkom trouwens, het is erg leerzaam :)

omniscale.nl


Verwijderd

Dat klinkt op zich heel erg goed en netjes (vooral dat cachen in de database, dat zet ik absoluut op de todo-lijst), maar wat maakt dit meer 'self contained' dan wat ik nu doe? Nu itereer ik ook door iedere directory en modulesettings.xml bevat alle informatie over die module. Aan de hand hiervan wordt het menu in het CMS gegenereerd en laadt een frontcontroller de juiste PHP-files (als iemand dus naar www.sitenaam.com/_admin/users/new gaat wordt de module users geladen).
Met self-contained bedoel ik dat je module (object/class) alle informatie bevat en ontsluit, zonder externe bestanden als XML, waar ik persoonlijk niet altijd blij van wordt, te gebruiken.

Mijn voorgestelde aanpak is natuurlijk ook in procedurele code mogelijk. Maar dat is allemaal afhankelijk van hoe jouw systeem verder in elkaar zit, is een module 1 bestand met functies die allemaal een prefix hebben en wordt er dan vanuit gegaan dat een overzichtsfunctie bijvoorbeeld news_getOverview heeft of news_getList , of beschijft juist de XML file waar bepaalde includes of functie staan ?

  • kluyze
  • Registratie: Augustus 2004
  • Niet online
posttoast schreef op woensdag 02 februari 2011 @ 00:08:
[...]

Kun je jouw PHP posten? Ik snap niet zo goed hoe je aan deze output komt.

[...]
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Walk through module-directories and find setting-files 
$modulesettingsFiles = array(); 
$it = new RecursiveDirectoryIterator("modules");
foreach (new RecursiveIteratorIterator($it) as $file){
    var_dump($file);
    echo('<br />');
    if ($file->getBasename() == 'modulesettings.xml'){ 
        $modulesettingsFiles[] = (string)$file;
        echo('found it<br />');
    }
} 
?>
Hetzelfde als jij doet dus. Wel op een Windows pc.

Volgens mij gaat file_exists kijken of een file bestaat. Waarschijnlijk gaat er ergens wel een iteratie gebeuren maar indien dat ergens onderliggend in het OS gebeurt gaat dit altijd veel sneller als wanneer php code gaat itereren.

  • moozzuzz
  • Registratie: Januari 2005
  • Niet online
posttoast schreef op woensdag 02 februari 2011 @ 00:08:
Is dat minder zwaar voor de server? Doet file_exists() niet gewoon hetzelfde wat ik doe eigenlijk? (geen retorische vraag, ik weet het echt niet)
Neen, in de voorgestelde verbetering kijk je per map naar één bestand (dat er wel of niet is). Jij kijkt naar alle bestanden (templates, phpfiles, andere dir) wat dus voor veel meer file-operaties zorgt.
Pagina: 1