Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Extract do public variables.
Forum PHP.pl > Forum > PHP > Object-oriented programming
Szymciosek
Witam,
wiem, że za pomocą extract($array) jestem w stanie stworzyć zmienne, a więc takie coś jak poniżej mi zadziała.

  1. <php
  2. class Test
  3. {
  4. public $array = array('first' => 'first variable');
  5.  
  6. public function __construct()
  7. {
  8. extract($this->array);
  9. echo $first; //output: first variable
  10. }
  11. }
  12. ?>


Lecz jak zrobić to tak żeby zmienne z extract weszły mi pod public $array czyli otrzymałbym coś takiego
  1. <?php
  2. class Test
  3. {
  4. public $array = array('first' => 'first variable');
  5.  
  6. //tutaj juz krok kolejny po wykonaniu extract()
  7. public $first = 'first variable';
  8.  
  9. public function __construct()
  10. {
  11. extract($this->array);
  12. echo $this->first; //to powinno działać tylko i wyłącznie po wykonaniu extract ze względu na dynamiczne tworzenie zmiennych.
  13. }
  14. }



Najlepiej chciałbym mieć możliwość stworzenia czegoś na wzór powyższego z dynamicznym tworzeniem zmiennych, ale w klasie statycznej tj:
  1. class Test
  2. {
  3. private static $array = array('first' => 'first variable'); //to byłoby pobierane z pliku, ale teraz jest array na potrzeby tego postu
  4.  
  5. public static function loadConfigFile()
  6. {
  7. //rozpoznanie srodowiska, w ktorym pracuje
  8. //ładowanie pliku z konfiguracją i tym samym tymi zmiennymi na podstawie rozpoznanego srodowiska
  9. //np. ładuje config_dev.yml
  10. self::createVariables();
  11. }
  12.  
  13. /*
  14.   * $_data => data from config file after parse
  15.   */
  16. private static function createVariables($_data)
  17. {
  18. //tutaj odbieram przekazane dane z pliku i chciałbym zrobić extract
  19. extract($_data);
  20. }
  21.  
  22. }

Teraz z innej klasy chciałbym mieć do tego dostęp tj:
  1. class Start
  2. {
  3. public function __construct()
  4. {
  5. Config::loadConfigFile();
  6. echo Config::$first;
  7. }
  8. }



Da się to jakoś zrobić?
Wiem, że się trochę rozpisałem, ale mam nadzieję, że jest to w jakimś stopniu zrozumiałe.

Pozdrawiam,
Szymon
Crozin
Ale po co tworzyć takiego paskudnego potworka, jak można utworzyć sobie prosty obiekt-kontener*, który będzie robił to samo, ale o wiele lepiej? Bo co za różnica czy napiszesz sobie:
  1. echo Config::$first;
  2. echo $config->get('first');
(pomijając fakt, że pierwsze rozwiązanie ma same wady, a drugie kilka sporych zalet).


* na dobrą sprawę, można sobie nawet rozszerzyć ArrayAccess, żeby uniknąć powielania istniejącego kodu.

EDIT: Drobna "literówka" się wkradła w post. wink.gif
EDIT2: Oczywiście to drugie rozwiązanie jest lepsze.
pyro
Tworzysz zmienną główną:

  1. class Config {
  2.  
  3. private $_data = array();
  4.  
  5. }


Następnie dodajesz __get i __set

  1. class Config {
  2.  
  3. private $_data = array();
  4.  
  5. public function __get($key) {
  6. return $this->_data[$key];
  7. }
  8.  
  9. public function __set($key, $value) {
  10. $this->_data[$key] = $value;
  11. }
  12.  
  13. }


Potem dodajesz swoje funkcje ładujące, które dodają dane do $_data, a potem się odwołujesz. Przykład:

  1. // PRZYKŁAD !
  2. $config = Config::instance();
  3. echo $config->maxAllowedUsers;
  4. echo $config->connectionTimeout;
  5. echo $config->somethingElse;
  6.  


// ADD

Cytat(Crozin @ 9.04.2013, 11:18:08 ) *
(pomijając fakt, że drugie rozwiązanie ma kilka sporych zalet, a drugie samo wady).


facepalmxd.gif
Szymciosek
  1. <?php
  2.  
  3. class Config
  4. {
  5. public $configFile = array('first' => 'first variable');
  6. public $data;
  7.  
  8. public function __construct()
  9. {
  10. //tworzenie instancji
  11. $this->createVariables();
  12. }
  13.  
  14. public function createVariables()
  15. {
  16. foreach ($this->configFile as $key => $value)
  17. {
  18. $this->$key = $value;
  19. }
  20. }
  21.  
  22. public function __get($_key)
  23. {
  24. return $this->data[$_key];
  25. }
  26.  
  27. public function __set($_key, $_value)
  28. {
  29. $this->data[$_key] = $_value;
  30. }
  31. }
  32.  
  33. var_dump(new Config());


Tylko teraz pytanie jak z utrzymaniem tych danych?
Skoro zrobię w Start.php instancję tej klasy i uruchomię ładowanie pliku, a później gdzieś indziej będę chciał wykorzystać te zmienne czyli np. w klasie Logout czyli praktycznie na samym końcu...

Jak znowu stworzę instancję tej klasy i dane będą zapisane, to super, ale co wtedy z wydajnością? Może trochę głupie pytania, nie mam za bardzo, gdzie teraz tego sprawdzić. Dopiero w domu.
Crozin
1. Wywal metody __get()/__set() na rzecz normalnych get()/set(). Obecne rozwiązanie nie przynosi Ci absolutnie żadnej korzyści, a tworzy kilka idiotycznych problemów oraz zmniejsza czytelność kodu.
2. Raz utworzony obiekt przekazujesz sobie tam gdzie go potrzebujesz - Google: dependency injection. Chociaż taki obiekt jak "Logout", raczej nie będzie potrzebował zależności do całego obiektu konfiguracji.
Szymciosek
1)
index.php
  1. <?php
  2. include_once 'Config.php';
  3.  
  4. $config = Config::getInstance();
  5. var_dump($config);
  6.  
  7. echo '<a href="logout.php">Logout</a>';


logout.php
  1. <?php
  2. include_once 'Config.php';
  3.  
  4. $config = Config::getInstance();
  5. echo $config->get('first');


Config.php
  1. <?php
  2.  
  3. <?php
  4.  
  5. class Config
  6. {
  7. public $configFile = array('first' => 'first variable');
  8. public $env = 'prod';
  9. public $data;
  10.  
  11. private static $_instance = NULL;
  12.  
  13. /*
  14.   * Creates instance of Config class
  15.   *
  16.   * @return $Config
  17.   */
  18. public static function getInstance()
  19. {
  20. if (self::$_instance === NULL)
  21. {
  22. self::$_instance = new self();
  23. }
  24. return self::$_instance;
  25. }
  26.  
  27. public function __construct()
  28. {
  29. $this->loadFile();
  30. $this->createVariables();
  31. }
  32.  
  33. public function loadFile()
  34. {
  35. switch ($this->env)
  36. {
  37. case 'prod' :
  38. $file = 'config_prod.yml';
  39. break;
  40. case 'dev' :
  41. $file = 'config_dev.yml';
  42. break;
  43. }
  44.  
  45. var_dump('Loaded file: ' . $file);
  46. }
  47.  
  48. /*
  49.   * Creates variables from the config file
  50.   */
  51. public function createVariables()
  52. {
  53. foreach ($this->configFile as $key => $value)
  54. {
  55. $this->set($key, $value);
  56. }
  57. }
  58.  
  59. public function get($_key)
  60. {
  61. return $this->data[$_key];
  62. }
  63.  
  64. public function set($_key, $_value)
  65. {
  66. $this->data[$_key] = $_value;
  67. }
  68. }


2) Co do dependency injection mam zamiar się wziąć powoli i tak ogólnie za wzorce projektowe, aczkolwiek nie wiem kiedy znajdę chwilę na to, bo matura niedługo.

Takie coś już jest dobrze wg Ciebie Crozin?
Crozin
Cytat
Co do dependency injection mam zamiar się wziąć powoli i tak ogólnie za wzorce projektowe, aczkolwiek nie wiem kiedy znajdę chwilę na to, bo matura niedługo.
Dependency Injection i inne podstawy OOP w 45 sekund:
1. Obiekt w miarę możliwości nie powinien odwoływać się do niczego co pochodzi spoza jego samego, tj. żadnych zmiennych globalnych czy odwołania do innych klas (patrz: Config::getInstance() czy zapisanie nazw plików wewnątrz klasy).
2. Biorąc sobie do serca punkt pierwszy, musimy dojść do wniosku, że wszelkie zależności muszą być przekazane przez argumenty metod.
2.1. Jeżeli jakaś zależność jest niezbędna do działania obiektu, powinna być przekazana już w konstruktorze.
2.2. Jeżeli jakaś zależność jest opcjonalna można dla niej utworzyć osobnego settera.
3. Każdy obiekt zajmuje się jedną rzeczą (czyli taki obiekt Config nie bawi się już w rozpoznawanie czy pracuje w środowisku produkcyjnym czy deweloperskim).
4. Gdzie się tylko da unikamy static (tutaj nie ma żadnej przesłanki sugerującej konieczność skorzystania z tego).
5. Dbamy o obsługę błędów (metoda get() powinna rzucić wyjątek InvalidArgumentException w przypadku odwołania się do nieistniejącego klucza).

Oczywiście nie są to jakieś nienaruszalne zasady, ale jeżeli je łamiesz musisz to robić świadomie i umieć sobie odpowiedzieć dlaczego warto je łamać.
Szymciosek
3. Czyli powinienem mieć osobną klasę do rozpoznania środowiska na wzór np.
  1. class Environment
  2. {
  3. public static $env = 'prod';
  4.  
  5. public static function getEnv()
  6. {
  7. return self::$env;
  8. }
  9.  
  10. //lub
  11. public static function getEnv()
  12. {
  13. switch (self::$env)
  14. {
  15. //case takie jak w config
  16. }
  17.  
  18. return $file;
  19. }


i teraz w Config
  1. public function loadConfigFile()
  2. {
  3. YamlReader.load(Environment::getEnv());
  4. }


5. To jest właśnie to, gdzie pewnie w wielu przypadkach brakuje i chyba wezmę to sobie do serca i poćwiczę jeszcze coś takiego.

Wrócę do tego później, bo za 13min jest dzwonek i koniec lekcji przy komputerze.

Dziękuję Ci bardzo za te rady - jeszcze popatrzę na przykłady DI.

Ale chyba Config jeszcze może zająć się ładowaniem konkretnego pliku konfiguracyjnego?
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.