Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Config
Forum PHP.pl > Forum > PHP > Object-oriented programming
aras785
Witam. Jak według was powinno pobierać się konfuguracje z plik php? A może lepsze do tego jest file.ini ?

U mnie obecnie wygląda to tak:

  1. <?php
  2. namespace Lib;
  3.  
  4. class Config {
  5. public static $con;
  6. public static function get($var) {
  7. require('Config/config.php');
  8. self::$con = $config[$var];
  9. return(self::$con);
  10.  
  11. }
  12. }


I w pliku php:

  1. $config['mysql'] = array (
  2. 'host'=>'localhost',
  3. [..]
  4. );
  5. [..]
!*!
Przejrzyj CakePHP, mają niezłe rozwiązanie np odczytujesz wartości przez

  1. Config:read('mysql.login');


Co odpowiada tablicy

Cytat
array('mysql' => array('login'=>'test'))


Nie potrzebnie tworzysz zmienną $config.

file.php
  1. return array();

  1. require_once 'Config/file.php';


I co jak będę chciał wczytać inny plik?
wujek2009
Wraz z każdym wywołaniem "get' (czyli pobrania danej konfiguracji) wczytujesz plik konfiguracyjny (Config/config.php) zmień require na require_once.
Najlepiej moim zdaniem byłoby stworzenie funkcji "instance" lub "init" (jak kto woli) i w tej funkcji byłoby wczytywanie konfiguracji (tj: require_once + zapis do zmiennej publicznej $con)

a w funkcji statycznej "get" po prostu zastosować zapis:
  1. public static function get($var, $default = NULL)
  2. {
  3. return array_key_exists($var, self::$con) ? self::$con[$var] : $default;
  4. }


Oczywiście funkcja instance/init musiałaby się uruchamiać wraz z aplikacja.
Crozin
1. Nie powinieneś stosować sztywnego odczytu z INI/XML/YAML. Raczej powinieneś skorzystać ze "sterownika", który wczyta dane z dowolnego formatu.
2. Dlaczego korzystasz z metod/klasy statycznej, skoro możesz, i zapewne będziesz miał, wiele różnych plików konfiguracyjnych?
3. Odwoływanie się wewnątrz jednego obiektu, do jakiegoś globalnego Config::get('xxx') to paskudna praktyka, która prędzej czy później zemści się.
Spawnm
Crozin, kolega ma problem z poprawnym napisaniem metody ładowania tablicy z pliku a ty mu każesz pisać sterowniki do ini/xml/yaml?
Proponował bym aby aras najpierw pogrzebał w kodzie innych fw, i dopiero wtedy brał się za swój jeśli nadal będzie czuł taką potrzebę.
!*!
Cytat(Crozin @ 25.02.2013, 20:27:32 ) *
Dlaczego korzystasz z metod/klasy statycznej, skoro możesz, i zapewne będziesz miał, wiele różnych plików konfiguracyjnych?

Jedno nie wyklucza drugiego.

Cytat(Crozin @ 25.02.2013, 20:27:32 ) *
3. Odwoływanie się wewnątrz jednego obiektu, do jakiegoś globalnego Config::get('xxx') to paskudna praktyka, która prędzej czy później zemści się.

W zasadzie to dlaczego? Globalny dostęp, jako singleton ma sens w przypadku plików konfiguracyjnych i jest dość przejrzysty.
CuteOne
!*! przyznał bym ci rację gdybyś zamienił "plików konfiguracyjnych" na "plik konfiguracyjny" - mała acz znacząca różnica.
aras785
Dzięki wszystkim za odpowiedzi. Zamierzam korzystać tylko z plików conf: *.php lub .ini . Nie chcę XML, YAML itd bo to mi do tego nie potrzebne. O co chodzi z tym:

Cytat
Odwoływanie się wewnątrz jednego obiektu, do jakiegoś globalnego Config::get('xxx') to paskudna praktyka, która prędzej czy później zemści się.


Jak mam to inaczej rozwiązać?
CuteOne
Np. rejestrując obiekt(y) (google -> registry pattern php)

edit: ściągnij sobie Zenda 1.xx i zobacz jak wygląda wgrywanie konfigów a co ważniejsze w jaki sposób, możesz nimi zarządzać
pyro
Pewnie chodziło mu o coś w stylu:

  1. class Blah {
  2.  
  3. public function something() {
  4.  
  5. Config::set('blah', 1);
  6.  
  7. }
  8.  
  9. }


Ale dlaczego to niby jest takie złe? Może często tego nie używam, ale jak już to zawsze znacznie ułatwiało niektóre sprawy zamiast je utrudniać, np. właśnie przy Config, Lang itp. Możliwe, że taki wzorzec projektowy ma już jakąś nazwę, ale sam go nazwałem Globalizator tongue.gif

// EDIT

Jakieś argumenty mile widziane.
CuteOne
Jasio wchodzi do sklepu aby kupić bułkę(main_config.ini), chleb(lang_config.ini) i 4l mleka(local_lang.ini), niestety w torbie(Singleton) Jasia jest miejsce tylko na jedną rzecz - co teraz pocznie biedny Jaś?

Użyje wzorca z mojego poprzedniego postu smile.gif
!*!
Dlatego Config::get() powinien pobierać dane z tablicy, a Config::load() ładować zawarotść plików do tablicy głównej (patrz cakephp).
pyro
Cytat(CuteOne @ 26.02.2013, 11:18:47 ) *
Jasio wchodzi do sklepu aby kupić bułkę(main_config.ini), chleb(lang_config.ini) i 4l mleka(local_lang.ini), niestety w torbie(Singleton) Jasia jest miejsce tylko na jedną rzecz - co teraz pocznie biedny Jaś?


  1. Config::load('main_config.ini', 'bulka');
  2. Config::load('lang_config.ini', 'chleb');
  3. Config::load('local_lang.ini', 'mleko');
  4.  
  5. // ...
  6.  
  7. echo Config::get('bulka.cena'); // 2 zł
  8. echo Config::get('mleko.cena'); // Fatal Error: Allowed memory size of 8388608 bytes exhausted ...


// EDIT

@!*! mnie wyprzedził.
CuteOne
mysql.ini
  1. resources.db.host = localhost


mssql.ini
  1. resources.db.host = 10.1.1.1


W akcji add() użyję mysql.ini po czym odpalam akcję add2() gdzie używam mssql.ini, następnie odpalam akcję add3() gdzie ponownie używam mysql.ini - cio wtedy?


ps. nie znam CakePHP ale dla mnie (tak na logikę) ładowanie 10 plików do jednego obiektu to lekkie przegięcie
!*!
Cytat(CuteOne @ 26.02.2013, 11:26:39 ) *
W akcji add() użyję mysql.ini po czym odpalam akcję add2() gdzie używam mssql.ini, następnie odpalam akcję add3() gdzie ponownie używam mysql.ini - cio wtedy?


Nic, bo mysql.ini już siedzi w tablicy dodany przez Config::load() wink.gif a skoro jest kilka mysql.ini to ścieżki się nie zgadzają i powinien być inny identyfikator.
pyro
To mozna zastosować rozwiązanie, ktore podałem powyżej i ładujesz tylko to co chcesz i w prosty sposób się do tego odwołujesz z dowolnego miejsca.
Crozin
Cytat
Crozin, kolega ma problem z poprawnym napisaniem metody ładowania tablicy z pliku a ty mu każesz pisać sterowniki do ini/xml/yaml?
Przecież to się ogranicza do 1-2 interfejsów i dwóch obiektów współpracujących ze sobą. Takie coś kompletny nowicjusz powinien być wstanie zrobić po pierwszym lepszym "wprowadzeniu w OOP".
Cytat
W zasadzie to dlaczego? Globalny dostęp, jako singleton ma sens w przypadku plików konfiguracyjnych i jest dość przejrzysty.
1. Poza nielicznymi przypadkami globalny dostęp do czegokolwiek jest raczej niepożądany.
2. Dostęp do konfiguracji przy pomocy singletona będzie niewiele czytelniejszy od zwykłego rozwiązania (IoC), a niesie za sobą standardowy zestaw problemów wynikających z tak ścisłego powiązania dwóch obiektów ze sobą.
Cytat
Zamierzam korzystać tylko z plików conf: *.php lub .ini . Nie chcę XML, YAML itd bo to mi do tego nie potrzebne.
Skoro już wiesz, że będziesz korzystać z PHP oraz INI skorzystanie z strategii jest w pełni uzasadnione. Szczególnie, zważywszy na fakt, że zapewne pliki INI będziesz chciał cache'ować.
!*!
Eydcja. A jak robisz zamieszanie z plikami to świadomie, poza tym nie widze problemu aby usunać inne mysql.ini Config::del('mysql') wink.gif zaraz po wczytaniu z "globalnego zasięgu".
CuteOne
Przy rozbudowanej aplikacji takie zamieszanie raczej jest uzasadnione smile.gif a usuwać i ponownie ładować te same dane zamiast użyć rejestru.. hmm mało optymalne
!*!
Cytat
1. Poza nielicznymi przypadkami globalny dostęp do czegokolwiek jest raczej niepożądany.

Z powodu?

Cytat
2. Dostęp do konfiguracji przy pomocy singletona będzie niewiele czytelniejszy od zwykłego rozwiązania (IoC), a niesie za sobą standardowy zestaw problemów wynikających z tak ścisłego powiązania dwóch obiektów ze sobą.


Jakich problemów?

To jest raczej kwestia tego, co jest dla Nas wygodniejsze. Pierwszy sposób nie ma przewagi nad drugim i odwrotnie.

Cytat
Przy rozbudowanej aplikacji takie zamieszanie raczej jest uzasadnione a usuwać i ponownie ładować te same dane zamiast użyć rejestru.. hmm mało optymalne

I tu się zgadzamy. choć nie wiem za bardzo, aby przytoczone tu rozwiązanie robiło burdel (chyba że jest źle napisane).
Crozin
@!*!: Globalny dostęp/sningleton niesie za sobą pewną konstrukcję, a mianowicie:
  1. public function doSth() {
  2. // jakaś tam metoda naszego obiektu
  3. $maxSth = Config::get('...max');
  4. // czy tam:
  5. $maxSth = Config::getInstance()->get('...max');
  6. // dalszy ciąg naszej metody
  7. }
Taka konstrukcja tworzy relację pomiędzy naszym obiektem, a klasą Config. Bezpośrednią (tj. opartą o klasę, a nie implementację interfejsu) relację, która w dodatku jest ukryta (nagle w środku kodu jest jakieś odwołanie). No to teraz lista problemów:
1. Nasz obiekt, który może nie mieć kompletne żadnego związku z resztą systemu, nagle staje się niepotrzebnie uzależniony od klasy Config. Kodu nie da się łatwo odizolować - tracimy jego uniwersalność/przenośność.
2. Skoro dla naszego obiektu nie da się wytworzyć pożądanego środowiska (ponieważ sam obiekt je definiuje), tak prosta rzecz jak chociażby testy (niekoniecznie jednostkowe) stają się nagle bardzo trudnym zadaniem.
3. Zdecydowanie trudniej jest utworzyć dwa obiekty o różnej konfiguracji, ponieważ ta jest na sztywno zapisana w klasie Config. Konieczne byłyby potworki typu:
  1. $...max = Config::get('...max');
  2. $myObject = new MyObject();
  3. Config::set('....max', 12);
  4. $myAnotherObject = new MyObject();
  5. Config::set('...max', $...max);

!*!
Cytat
1. Nasz obiekt, który może nie mieć kompletne żadnego związku z resztą systemu, nagle staje się niepotrzebnie uzależniony od klasy Config. Kodu nie da się łatwo odizolować - tracimy jego uniwersalność/przenośność.

Albo rybki, albo akwarium. Jak piszesz coś co ma być publiczne, to raczej używasz czystego PHP lub jeśli jest dedykowane na jakiś FW to ichniejszych metod. To że są zależności to raczej normalne i trudno by było zbudować coś większego bez nich. Już nie wspominając że na "czysto" raczej się przez takie narzędzia danych nie ustala, a raczej przez parametry metody i nawet jakby Config::get/set() w niej istniał, to ustawienie parametru nie musi go wykonać.

Cytat
3. Zdecydowanie trudniej jest utworzyć dwa obiekty o różnej konfiguracji, ponieważ ta jest na sztywno zapisana w klasie Config. Konieczne byłyby potworki typu:


To samo co wyżej. Jak potrzebujesz konfiguracji, trudno aby obiekty były gołe bez możliwości wgrania CFG z zewnątrz.
aras785
Dzięki. Czyli mam zrobić na zasadzie load, get...

Metoda load moze wygladac tak:

  1. <?php
  2. class Config {
  3. public static $configs=array();
  4. public static function load($file) {
  5. return self::$configs[] = parse_ini_file('Config/'.$file,true);
  6. }
  7.  
  8. }

questionmark.gif

Przepraszam za amatorkę w moich kodach ale staram się jak tylko potrafie
Spawnm
Jeśli już to:
self::$configs[$file] = parse_ini_file('Config/'.$file.'.ini',true);
Crozin
Cytat
To że są zależności to raczej normalne i trudno by było zbudować coś większego bez nich.
Zależności są nieuniknione i same w sobie nie są absolutnie niczym złym. Chodzi o to by zależności były luźne, oparte na interfejsach, nie sztywne, pracujące na klasach. Pierwsze podejście ma właściwie same zalety, jest naturalne dla OOP - Google: IoC pros. Drugie powoduje masę problemów. Jedną, jedyną zaletą drugiego rozwiązania jest jego zwięzłość - przy małych czy "śmieciowych" projektach dobra architektura systemu zwyczajnie w świecie nie ma znaczenia.
Cytat
To samo co wyżej. Jak potrzebujesz konfiguracji, trudno aby obiekty były gołe bez możliwości wgrania CFG z zewnątrz.
Nie zrozumiałeś sensu podanego przeze mnie przykładu. Chodzi o problemy z ustawieniem zewnętrznego środowiska (tutaj konfiguracji) w przypadku ścisłych zależności, zresztą chyba wyższość jednego rozwiązania nad drugim jest widoczna przy bezpośrednim zestawieniu:
  1. $config = new Config(...);
  2. $someObject = new MyObject($config);
  3.  
  4. $modifiedConfig = new Config($config);
  5. $modifiedConfig->set('...', '...');
  6. $modifiedConfig->set('...2', '...2');
  7. $anotherObject= new MyObject($modifiedConfig);
  8.  
  9. // powiedzmy, że metoda doSth pobiera coś z conifga, np. $config->get('default_value') gdy nie podamy żadnego argumentu.
  10. $someObject->doSth();
  11. $anotherObject->doSth();
  12.  
  13. // -----
  14.  
  15. $someObject = new MyObject(); // fajniej?
  16. $anotherObject = new MyObject(); // co tutaj? Modifikacja globalnego Config? Jeszcze da się to zrobić (patrz: mój wcześniejszy post)
  17.  
  18. // tutaj jesteśmy już w tzw. ciężkiej dupie ;)
  19. $someObject->doSth();
  20. $anotherObject->doSth();
Cytat
Albo rybki, albo akwarium. Jak piszesz coś co ma być publiczne, to raczej używasz czystego PHP lub jeśli jest dedykowane na jakiś FW to ichniejszych metod.
Jeżeli FW nie jest napisany w taki sposób, jak tutaj się sugeruje, to można mieć i rybki i akwarium niedużym/zerowym nakładem pracy.
sazian
ja bym raczej dał tak
  1. public static function load($file) {
  2. if(!isset(self::$configs[$file]))
  3. {
  4. self::$configs[$file]= parse_ini_file('Config/'.$file,true);
  5. }
  6. return self::$configs[$file];
  7.  
  8. }

dzięki tamu unikasz wielokrotnego wczytywania i parsowania pliku
!*!
Crozin - przeczytałem, to wczoraj... czytam to dzisiaj, razem z linkami i nadal nie widzę wyższości jednego nad drugim o której piszesz.
Kwestia indywidualnego podejścia do projektowania samych bebechów, tudzież wygody.
aras785
Mam sobie tablice w pliku config:

  1. <?php
  2. return array(
  3. 'mysql'=>array(
  4. 'driver'=>'mysql',
  5. 'host'=>'localhost',
  6. 'dbname'=>'album',
  7. 'login'=>'root',
  8. 'password'=>'vertrigo'
  9. )
  10. );


I wczytuje go: Config::load('plik');

//funckja load:
  1. public static function load($file) {
  2. if(!isset(self::$configs[$file]))
  3. {
  4. self::$configs[$file]= include_once('Config/'.$file.'.php');
  5. }
  6. return self::$configs[$file];
  7. }


I jak zrobić aby za pomocą get pobiera dowolny ciąg czyli:
  1. Config::get('plik.mysql.host'); //wyswietli localhost


Lub
  1. Config::get('plik.mysql'); //wyswietli tablice


Wiem, ze trzeba podzielić argument który podajemy funkcji get funckją explode, a później jak to dynamicznie obrobić? Bo łatwo jest sobie zrobc return self::$cos[$explode[0]][$explode[1]][$explode[2]]; //to dla pierwszego przykladu.

Proszę o jakieś pomysły bo póki co korzystam z parse ini ale to siedzi cały czas w mojej głowie... Pozdrawiam

Ktoś da jakąś wskazówkę?
!*!
Kiedyś napisałem coś takiego:

  1. public static function get($name = null)
  2. {
  3. if(null === $name)
  4. {
  5. return self::$configs;
  6. }
  7.  
  8. $name = trim($name, '.');
  9.  
  10. if(isset(self::$configs[$name]))
  11. {
  12. return self::$configs[$name];
  13. }
  14.  
  15. $cfg = &self::$configs;
  16.  
  17. foreach(explode('.', $name) as $key)
  18. {
  19. if(isset($cfg[$key]))
  20. {
  21. $cfg = &$cfg[$key];
  22. }
  23. else
  24. {
  25. return null;
  26. }
  27. }
  28. return $cfg;
  29. }


Zresztą, wspomniałem już żebyś się zapoznał z cakePHP, mechanizm jest bliźniaczy.
sazian
wiem że bardzo "statycznie" ale to tylko pokazówka
  1. class conf{
  2.  
  3. public static $configs;
  4. public static function path($array, $path, $default = NULL, $delimiter = NULL)
  5. {
  6. do
  7. {
  8. $key = array_shift($path);
  9. if (isset($array[$key]))
  10. {
  11. if (!empty($path))
  12. {
  13. if (is_array($array[$key]))
  14. {
  15. $array = $array[$key];
  16. }
  17. else
  18. {
  19. break;
  20. }
  21. }
  22. else
  23. {
  24. return $array[$key];
  25. }
  26. }
  27. else
  28. {
  29. break;
  30. }
  31.  
  32.  
  33. }
  34. while (!empty($path));
  35. return $default;
  36. }
  37. public static function load($group)
  38. {
  39.  
  40.  
  41. $path = explode('.', $group);
  42. return conf::path(self::$configs, $path, NULL, '.');
  43.  
  44.  
  45.  
  46. }
  47. }
  48. conf::$configs=array('x'=>array('y'=>array('z'=>155)));
  49. var_dump(conf::load('x.y.z'));
ano
Cytat(!*! @ 27.02.2013, 10:12:01 ) *
Crozin - przeczytałem, to wczoraj... czytam to dzisiaj, razem z linkami i nadal nie widzę wyższości jednego nad drugim o której piszesz.
Kwestia indywidualnego podejścia do projektowania samych bebechów, tudzież wygody.


Nie, to nie kwestia indywidualnego podejścia. To kwestia pewnych standardów, wypracowanych reguł w inżynierii oprogramowania. Za @Crozinem - oczywiście jeżeli robisz małe projekty to nie uświadczysz większej różnicy. Ale czemu od początku nie robić tego dobrze?

Polecam: (w tym przypadku najlepiej od slajdu 6)
http://www.slideshare.net/fabpot/dependenc...ion-in-php-5354
!*!
W tym przypadku słowo "standard" to lekkie nadużycie.
Dobra prezentacja, o to właśnie mi chodziło, aby np. Klasa User miała sama w sobie możliwość ustalenia konfiguracji, ale ni jak ma to się do samej klasy odczytu CFG.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.