Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Przechwycenie momentu utworzenia zmiennej
CuteOne
post 9.11.2012, 16:07:43
Post #1





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Witam,

na wstępie trochę kodu

1.
  1.  
  2. class A {
  3.  
  4. public function xyz() {
  5.  
  6. $this->a = 'aaa';
  7. }
  8. }
  9.  
  10. $a = new A();
  11. $a -> xyz();


2.
  1.  
  2. class A {
  3.  
  4. public $a='bbb';
  5.  
  6. public function xyz() {
  7.  
  8. $this->a = 'aaa';
  9. }
  10. }
  11.  
  12. $a = new A();
  13. $a -> xyz();

i pytanie - czy istnieje, możliwość przechwycenia z zewnątrz, momentu zainicjowania(1) $this->a lub zmiany jego wartości(2)?

EDIT: __set() nie wchodzi w grę

Ten post edytował CuteOne 9.11.2012, 16:18:09
Go to the top of the page
+Quote Post
Crozin
post 9.11.2012, 16:46:16
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Utwórz setter, który tuż po nadaniu wartości utworzy zdarzenie, które poinformuje o zmianie wartości wszystkich subskrybentów. Google: php observator/event dispatcher.
Go to the top of the page
+Quote Post
CuteOne
post 9.11.2012, 16:58:56
Post #3





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Właśnie chciałem uniknąć obserwatora ;P ale wydaje mi się, że ten sposób będzie najlepszy. Jeszcze jedno pytanie jeśli, można - co w przypadku zmiennych lokalnych danej metody:
  1. class A {
  2.  
  3. public function xyz() {
  4.  
  5. $a = 'aaa'; //notify
  6. $a = 'bbb'; //notify
  7. }
  8. }


Kombinowałem z closures http://www.php.net/manual/en/functions.anonymous.php#95124 ale wtedy dana zmienna staje się zależna od wartości zewnętrznych

Ten post edytował CuteOne 9.11.2012, 16:59:38
Go to the top of the page
+Quote Post
Crozin
post 9.11.2012, 17:14:21
Post #4





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Dlaczego chciałeś uniknąć obserwatora czy event dispatchera?
Cytat
Jeszcze jedno pytanie jeśli, można - co w przypadku zmiennych lokalnych danej metody
Najzwyczajniej w świecie odczep się od nich. wink.gif Zmienne lokalne danej metody są na wyłączny jej użytek i nie powinny mieć wpływu na cokolwiek poza wnętrzem metody.

Opisz może co jest faktycznym problemem, bo wygląda na to, że mamy tutaj do czynienia z problemem XY.
Go to the top of the page
+Quote Post
CuteOne
post 10.11.2012, 14:49:47
Post #5





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Tworzę system pluginów na bazie Zenda. Z tą różnicą, że pluginem, może być nie tylko klasa rozszerzająca "Zend_Plugin_Abstract" ale cały moduł z kontrolerami, modelami i wszystkim tym co zawiera standardowy moduł + własne pluginy(rozszerzające Zend_Plugin_Abstract). Innymi słowy moduły mogą być rozszerzane przez inne moduły(i ich pluginy), które mogą być dalej rozszerzane i tak w nieskończoność...


Uproszczony przykład dwóch niezależnych od siebie modułów - "pseudopluginów"
  1. class Comments_IndexController extends Zend_Controller_Action {
  2.  
  3. public function viewAction() {
  4.  
  5. $id = $this->_getParam('id');
  6.  
  7. $model = new Comments_Model_XYZ();
  8. $row = $model -> find($id);
  9.  
  10. $this->view->comment = $row;
  11. }
  12. }
  13.  
  14.  
  15. class BBCode_ParseController extends Zend_Controller_Action {
  16.  
  17. public function view($comment) {
  18.  
  19. $this->view->comment = new BBCode_Model_Prepare($comment);
  20. }
  21.  
  22. public function strip($row) {
  23.  
  24. return explode('#', strip_tags(implode('#', $row)));
  25. }
  26. }


oraz ich konfiguracja.
list - lista zainstalowanych pluginów,
action - definicja zależności pomiędzy pluginami
  1. {
  2. "list": [ // lista pluginów/modułów
  3. {
  4. "name": "Comments",
  5. "version": "1.00",
  6. "weight": 8,
  7. "active": true
  8. },
  9. {
  10. "name": "BBCode",
  11. "version": "1.00",
  12. "weight": 7,
  13. "active": true,
  14. "depend": [
  15. {"name": "Comments", "version": ">=1.00"}
  16. ]
  17. }
  18. ],
  19. "action": [ // preDispatch i postDispatch - przed odpaleniem metody i po odpaleniu metody
  20. {
  21. "use": "BBCode",
  22. "hooks": [
  23. // odbierz zwrócone dane z Comments->view i 'wepchnij' do BBCode->view jako parametr
  24. {"hook_method": "BBCode->view after Comments->view", "hook_var": "[@return]"},
  25. // odbierz $row po jego zainicjowaniu i wepchnij do BBCode->strip jako parametr
  26. {"hook_method": "BBCode->strip around Comments->view", "hook_var": "[@var $row]"}
  27. ]
  28. }
  29. ]
  30. }


Dla zwiększenia, możliwość ingerowania w dane i funkcjonalność, danej metody zastosowałem pseudo AOP (before, after, around). Before i after mam już rozwiązane, problem to around czyli możliwość ingerencji w metodę podczas jej wykonywania np. w zmienną $row przed lub po jej zainicjowaniu.
Go to the top of the page
+Quote Post
Crozin
post 10.11.2012, 15:34:25
Post #6





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Nawet AOP nie zakłada takiego pogwałcenia kolejności wykonywania kodu jak ingerencja we wnętrze jakiejś metody.

Według mnie powinieneś tutaj skorzystać z event dispatchera i tworzyć bardzo dużo zdarzeń, gdzie część z nich będzie miała możliwość modyfikowania obiektu, którego dane zdarzenie dotyczy. Dzięki temu będziesz mógł bez problemu podpiąć np. BBCode* przez proste podpięcie go pod odpowiednie zdarzenia w systemie.

* zlituj się nad swoimi użytkownikami i nie katuj ich tym paskudnym i niewygodnym formatem.
Go to the top of the page
+Quote Post
CuteOne
post 10.11.2012, 20:47:04
Post #7





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Zrobiłem małą inwentaryzację swoich pomysłów "jak pluginować pluginy" i doszedłem do kilku wniosków:
- pisanie nowych pluginów musi być równie proste jak pisanie zwykłych modułów (ameryki nie odkryłem ale to chyba będzie największe wyzwanie przy pisaniu systemu)
- każdy kontroler, model czy widok musi posiadać odgórnie wyznaczone miejsca dostępu (hooki) dla danych wrażliwych(widać bez obserwatora się nie obejdzie wink.gif )
- każdy moduł(pseudopligin) musi posiadać własne API, dzięki czemu możliwe będzie sterowanie modułem przy użyciu innego modułu. Nad tą kwestią muszę się jeszcze zastanowić - co, jak i gdzie smile.gif
- idąc Twoją radą zrezygnuje z możliwości manipulowania zmiennymi danych metod.
- rozszerzanie pluginu przez inny plugin (tak jak w akcji Comments->viewAction() => BBCode->view()), będzie możliwe poprzez:
1. Nadpisanie metody
2. Hooki
3. Before i After
4. Za pomocą serializacji obiektu i zmianach struktury przed jego deserializacją (do przemyślenia)
5. Za pomocą API

Zostało jeszcze parę(naście) kwestii do przemyślenia ale to już pierdółki. Teraz zastanawiam się w jaki sposób uaktualniać już zainstalowane pluginy - wsteczna kompatybilność i te sprawy. Gdyby istniała, możliwość dziedziczenia metod rodzica przez dziecko bez ingerencji w kod..

Co do BBCode to tylko przykład smile.gif wybrałem go bo najlepiej obrazuje w jaki sposób jeden plugin, może ingerować w dane innego pluginu.

Pozdrawiam i dziękuję za Twoje rady

Edit: co do Event Dispatchera to nie mam na jego temat wystarczającej wiedzy abym mógł napisać "profesjonalny" system - pracuje na Zend 1.x.x, może za jakiś czas zagłębie się w niego dokładniej gdy będę przechodził na Zenda 2.

Ten post edytował CuteOne 10.11.2012, 21:34:04
Go to the top of the page
+Quote Post
Crozin
post 11.11.2012, 15:45:24
Post #8





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Nie do końca rozumiem co dokładnie chcesz stworzyć (to chyba przez to, że słowa plugin/moduł mają po 1000 znaczeń :]) ale...
Cytat
każdy kontroler, model czy widok musi posiadać odgórnie wyznaczone miejsca dostępu (hooki) dla danych wrażliwych(widać bez obserwatora się nie obejdzie )
Odgórne narzucanie czegokolwiek lubi się mścić.
Cytat
- rozszerzanie pluginu przez inny plugin (tak jak w akcji Comments->viewAction() => BBCode->view()), będzie możliwe poprzez:
1. Nadpisanie metody
2. Hooki
3. Before i After
4. Za pomocą serializacji obiektu i zmianach struktury przed jego deserializacją (do przemyślenia)
5. Za pomocą API
1. Nadpisywanie niemal zawsze jest najgorszym sposobem rozszerzania o dodatkowe, potencjalnie niepowiązane funkcje.
4. Nawet się nie waż takich głupot robić.
5. Nie rozumiem co masz na myśli tutaj przez API (znowu, 1000 znaczeń tego słowa).

Zadbaj o to by w systemie było dużo zdarzeń, przykładowo:
  1. $comment = /* pobierz obiekt komentarza, np. z formularza */;
  2.  
  3. $event= new GenericPrePersistEvent($comment);
  4. $eventDispatcher->dispatch('jakas.unikalna.nazwa.zdarzenia.np.comment.pre_persist', $event);
  5.  
  6. $comment = $event->getObject(); // zwraca obiekt, który mógł zostać zmodyfikowany (np. treść mogła zostać potraktowana parserem BBCode)
  7.  
  8. /* zapisz obiekt $comment w bazie danych */
  9.  
  10. $event = new GenericPostPersistEvent($comment);
  11. $eventDispatcher->dispatch('comment.post_persist', $event);
Takie proste rozwiązanie, powinno sprawdzić się w zdecydowanej większości przypadków. Nie tworzy ono też żadnych powiązań pomiędzy "głównym kodem" a pluginami.

Cytat
Teraz zastanawiam się w jaki sposób uaktualniać już zainstalowane pluginy - wsteczna kompatybilność i te sprawy. Gdyby istniała, możliwość dziedziczenia metod rodzica przez dziecko bez ingerencji w kod..
W zależności od tego, jak solidne/rozbudowane narzędzie chcesz uzyskać, a: oprzyj to o zwykły menadżer zależności (np. PHP-owski Composer), b: nie mam pojęcia czy istnieje jakiś odpowiednik w PHP (pewnie nie), ale pogoogleaj za Java OSGi - bardziej za architekturą i sposobie działania.

Cytat
Edit: co do Event Dispatchera to nie mam na jego temat wystarczającej wiedzy abym mógł napisać "profesjonalny" system - pracuje na Zend 1.x.x, może za jakiś czas zagłębie się w niego dokładniej gdy będę przechodził na Zenda 2.
A po co niby pisać samemu coś co już jest? Ot, chociażby ten dostępny w Symfony: https://github.com/symfony/EventDispatcher
Go to the top of the page
+Quote Post
CuteOne
post 11.11.2012, 18:51:46
Post #9





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Cytat
Nie do końca rozumiem co dokładnie chcesz stworzyć (to chyba przez to, że słowa plugin/moduł mają po 1000 znaczeń :]) ale...

Musze zaprojektować system ERP a właściwie podstawkę, którą można dowolnie rozszerzać. Taki system z reguły składa się z kilkunastu niezależnych od siebie elementów np. HR, work flow, crm. Elementy te są na tyle "duże", że zastosowanie zwykłego systemu pluginów odpada, dlatego pomyślałem aby pluginem mógł być pojedynczy moduł np.
Kod
modules
   default
      |-controllers
      |-models
      |-views
   hr
      |-controllers
      |-models
      |-views
      |-plugins
   crm
      |-controllers
      |-models
      |-views


Na etapie projektowania tak rozbudowanego systemu nie jestem w stanie przewidzieć zależności pomiędzy poszczególnymi modułami np. Work Flow, może być użyty w CRM'ie, statystyki sprzedaży z CRM'a mogą być użyte w HR do generowania raportów, raporty z HR mogą być wykorzystane przez Work Flow itd. Sprawę dodatkowo komplikuje fakt, że moduły będą pisane przez zupełnie nie znanych mi programistów a więc system musi być przejrzysty, intuicyjny i przede wszystkim na tyle "łatwy" aby pisanie dodatkowych modułów nie była przeprawą rzez piekło.

Cytat
Odgórne narzucanie czegokolwiek lubi się mścić.

Pewnie tak smile.gif ale jakoś muszę udostępnić miejsca, w których klasa A będzie mogła zmienić model w klasie B bez ingerencji w kod (jeden z wymogów projektu..).

Cytat
1. Nadpisywanie niemal zawsze jest najgorszym sposobem rozszerzania o dodatkowe, potencjalnie niepowiązane funkcje.
4. Nawet się nie waż takich głupot robić.
5. Nie rozumiem co masz na myśli tutaj przez API (znowu, 1000 znaczeń tego słowa).

1. Teraz gdy zobaczyłem Twój przykład z dispatcherem to faktycznie nadpisywanie nie miało by sensu
4. Gdzieś widziałem przykład wykorzystania serializacji do zmian w strukturze klasy podczas aktualizacji i powiem ci, że byłem wniebowzięty takim podejściem do problemu ;P
5. API:
  1. class CRM {
  2.  
  3. public function view() {
  4.  
  5. $api_wf = new API_WorkFlow();
  6. $api_wf -> setId(1);
  7.  
  8. $raport = $api_wf -> getRaport() //-> generateCSV(); - dodatkowa metoda zmieniająca kontekst danych
  9. }
  10. }
  11.  
  12. class Model_WorkFlow {
  13.  
  14. public function getRaport() {
  15.  
  16. // zwraca obiekt raportu
  17. }
  18.  
  19. public function setName($name) {
  20.  
  21. $this->entity->name = $name;
  22. }
  23. }
  24.  
  25. class Plugin_WorkFlow {
  26.  
  27. public function generateCSV() {
  28.  
  29. // zwraca string w formacie csv
  30. }
  31. }
  32.  
  33. class API_WorkFlow {
  34.  
  35. public function setId($id) { $this->id = $id; }
  36.  
  37. public function getRaport($params=array()) {
  38.  
  39. //prosty przykład wykorzystania API
  40. if(isset($params['view']) && $param['view'] == 'json') {
  41.  
  42. return new Model_WorkFlow_JSON($this->id);
  43. }
  44. else {
  45.  
  46. $model = new Model_WorkFlow();
  47. return $model -> getRaport();
  48. }
  49. }
  50.  
  51. public function editName(Model_WorkFlow_RaportInterface $raport, $name) {
  52.  
  53. $model = new Model_WorkFlow($raport);
  54. $model -> setName($name);
  55. $model -> save();
  56. }
  57. }
  58.  
  59. $crm = new CRM();
  60. $crm -> view();

W ten sposób mamy dostęp do zdefiniowanych w API metod bez bezpośredniego wywoływania modeli, kontrolerów itp. z innych modułów.

Cytat
W zależności od tego, jak solidne/rozbudowane narzędzie chcesz uzyskać, a: oprzyj to o zwykły menadżer zależności (np. PHP-owski Composer), b: nie mam pojęcia czy istnieje jakiś odpowiednik w PHP (pewnie nie), ale pogoogleaj za Java OSGi - bardziej za architekturą i sposobie działania.

Pogooglam dzięki za wskazówkę

Ten post edytował CuteOne 12.11.2012, 09:10:39
Go to the top of the page
+Quote Post
Crozin
post 12.11.2012, 17:57:53
Post #10





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
Pewnie tak ale jakoś muszę udostępnić miejsca, w których klasa A będzie mogła zmienić model w klasie B bez ingerencji w kod (jeden z wymogów projektu..).
Standardowe rozwiązanie tego problemu, które od lat się całkiem dobrze sprawdza, to IoC (zrealizowane np. przez DIC-a) + IDD. Jeżeli tylko swój własny kod będziesz pisał tak jakby był on przeznaczony jako tzw. 3rd-party-code, nie powinieneś mieć problemów - chociaż jest to bardziej czasochłonne, a kod czasami komplikuje się na dłuższą metę może przynieść to sporo oszczędności (czasu i pieniędzy).
Cytat
Gdzieś widziałem przykład wykorzystania serializacji do zmian w strukturze klasy podczas aktualizacji i powiem ci, że byłem wniebowzięty takim podejściem do problemu ;P
"Oryginalne" podejście do tematu niemal zawsze oznaczać będzie złe podejście.
Go to the top of the page
+Quote Post
CuteOne
post 15.11.2012, 09:35:22
Post #11





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


Twoje rady jak zawsze trafiają w dziesiątkę! Dziękuje bardzo za pomoc, teraz muszę ponownie zrobić remanent swoich pomysłów - zajmie to pewnie z tydzień, dwa. W razie wątpliwości mam nadzieję, że użyczysz mi swojej fachowej wiedzy.

Jeszcze raz dziękuję i pozdrawiam


Edit:

Wstępny projekt architektury aplikacji już mam

Legenda:
- EventDispatcher pochodzi z Symfony2
- ED skrót od EventDispatchera
- Plugin Manager - rejestruje wszystkie akcje pluginów do ED
- Resource Manager - standardowy Zendowski loader (lekko zmodyfikowany)
- ViewAction - renderowanie widoku

Kolejność ładowania klas od lewej do prawej, z góry na dół.

Nadal jednak nie rozwiązałem problemu zmiany modeli ;P W teorii bez problemu można wstrzyknąć modelX, Y, Z ale przy rozbudowanych akcjach będzie to wyglądało dość komicznie
  1. $class = new IndexController();
  2. $class -> viewAction(
  3. new ModelA,
  4. new ModelB,
  5. new ModelC,
  6. new ModelD,
  7. new ModelE
  8. ),
  9. new ServiceA,
  10. new ServiceB,
  11. new ServiceC
  12. )
  13. );

Drugim problem jest brak kontroli nad wstrzykiwanymi modelami
  1. // Tak jest elegancko
  2. interface IndexInterface {
  3.  
  4. public function viewAction(ModelX $modelX, ModelY $modelY);
  5. }
  6.  
  7. // a tu nie bardzo :)
  8. interface IndexInterface {
  9.  
  10. public function viewAction(array $models, array $services);
  11. }



Dzisiaj poświęcę parę godzin nad studiowaniem wzorców, może znajdę rozwiązanie tego problemu

Ten post edytował CuteOne 15.11.2012, 09:36:07
Go to the top of the page
+Quote Post
Crozin
post 15.11.2012, 14:14:50
Post #12





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Ale dlaczego chcesz dla danego obiektu wstrzykiwać inne obiekty przy pomocy tablicy, a nie jak sam pokazałeś, jako osobne parametry?
Go to the top of the page
+Quote Post
CuteOne
post 16.11.2012, 12:19:56
Post #13





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


tongue.gif

W sumie to sam nie wiem. Ubzdurałem sobie aby wstrzykiwać modele pakietami
Kod
{
   "use": "pluginXYZ",
   "inject": [
        {"models":["X", "Y"]},
        {"services": ["xx", "yyy"]}
   ],

   //inny plugin, który chce skorzystać z pluginXYZ
   "use": "pluginXYZ",
   "inject": [
        {"models":["V", "C", "E", "H", "H2"]},
        {"services": ["xx", "yyy"]}
   ]
}

viewAction($pakiet_models, $pakiet_services);

Dopiero po rozpisaniu tego na kartce wyszły ewentualne niedopatrzenia, więc zrezygnowałem z tablic na rzecz kontenerów
Kod
{
   "use": "pluginXYZ",
   "inject": [
        {"models": "ModelX_Container"},
        {"services": "ServiceX_Container"}
   ],

   //inny plugin, który chce skorzystać z pluginXYZ
   "use": "pluginXYZ",
   "inject": [
        {"models": "ModelZZ_Container"},
        {"services": "ServiceZZ_Container"}
   ]
}

  1. public function viewAction($model_container, $service_container) {
  2.  
  3. //po interfejsie modelu
  4. if($model_container->has('ModelX_Interface')) {
  5. $modelX = $model_container -> get('ModelX_Interface');
  6. }
  7. //lub wszystkie danego typu (kolekcji)
  8. if($model_container->has('Models_Domain_Interface')) {
  9. $models = $model_container -> getDomain('Models_Domain_Interface');
  10. }
  11. //lub po nazwie modelu
  12. if($model_container->has('ModelX')) {
  13. $modelX = $model_container -> get('ModelX');
  14. }
  15. }


Ten post edytował CuteOne 16.11.2012, 12:24:11
Go to the top of the page
+Quote Post
Crozin
post 16.11.2012, 12:54:44
Post #14





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


1. Po co jakieś dziwne rozgraniczenie na modele i usługi? A jak będziesz chciał wstrzyknąć jeszcze jakiś zupełnie oderwany od nich obiekt? Zresztą usługi pochodzą najczęściej z warstwy modelu.
2. Ponownie - zamiast wymyślać własne rozwiązanie, skorzystaj z gotowego i na 99% lepszego rozwiązania, np. kontenera dostępnego w Symfony2
Go to the top of the page
+Quote Post
CuteOne
post 16.11.2012, 15:20:36
Post #15





Grupa: Zarejestrowani
Postów: 2 958
Pomógł: 574
Dołączył: 23.09.2008
Skąd: wiesz, że tu jestem?

Ostrzeżenie: (0%)
-----


1. Tak jest skonstruowany Zend a nie mam za wiele czasu na tego typu mieszanie w kodzie
2. Coraz bardziej skłaniam się do nauki S2 i stosowania go w późniejszych aplikacjach..
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 31.07.2025 - 10:34