Autor wpisu: batman, dodany: 21.03.2011 08:00, tagi: php
Omawiając interfejsy dostępne w ramach biblioteki SPL, celowo pominąłem dwa z nich – SplObserver oraz SplSubject. Zrobiłem to dlatego, by ich opis nie zaginął w gąszczu pozostałych interfejsów. Dlaczego akurat te dwa zasłużyły na osobny wpis? Ponieważ przy ich pomocy możemy zaimplementować wzorzec obserwatora.
Nudna teoria
Wzorzec obserwatora należy do grupy wzorców czynnościowych. Wykorzystywany jest do powiadomienia jednego obiektu (obserwatora) o zmianie stanu drugiego (obserwowanego). Powiązane w ten sposób obiekty nie muszą być od siebie zależne, co więcej, nie muszą być świadome swojego istnienia. W ten sposób oba obiekty można niezależnie od siebie rozszerzać, nie wypływając na działanie obiektu powiązanego.
Akademicki przykład
Zacznijmy od prostego przykładu, w którym mamy dwa obiekty obserwujące i jeden obserwowany.
class Obserwator1 implements SplObserver { public function update(SplSubject $subject) { echo 'aktualizacja trafiła do klasy Obserwator1' . PHP_EOL; } } class Obserwator2 implements SplObserver { public function update(SplSubject $subject) { echo 'aktualizacja trafiła do klasy Obserwator2' . PHP_EOL; } } class Obserwowany implements SplSubject { protected $_observers = array(); public function attach(SplObserver $observer) { $this->_observers[spl_object_hash($observer)] = $observer; } public function detach(SplObserver $observer) { unset($this->_observers[spl_object_hash($observer)]); } public function notify() { foreach($this->_observers as $observer) { $observer->update($this); } } } $obserwator1 = new Obserwator1(); $obserwator2 = new Obserwator2(); $obserwowany = new Obserwowany(); $obserwowany->attach($obserwator1); $obserwowany->attach($obserwator2); $obserwowany->notify();
Wyniku działania powyższego kodu jest prosty do przewidzenia.
Jeśli zajdzie taka potrzeba, możemy odpiąć obiekt obserwatora i ponownie wysłać informację o aktualizacji stanu obiektu.
$obserwowany = new Obserwowany(); $obserwowany->attach($obserwator1); $obserwowany->attach($obserwator2); $obserwowany->notify(); $obserwowany->detach($obserwator1); $obserwowany->notify();
Przykład z życia wzięty
Przykłady akademickie mają to do siebie, że nijak nie pasują do twardej rzeczywistości i bardziej przeszkadzają niż pomagają w zrozumieniu zagadnienia. Dlatego też do lepszego poznania wzorca obserwatora posłużymy się praktycznym przykładem.
Załóżmy, że mamy aplikację, w której użytkownicy mogą zakładać konta oraz się na nie logować. Zalogowany użytkownik może zmienić swoje dane. Dane te od razu powinny trafić do obiektu sesji, by przy kolejnym żądaniu można było pobrać aktualne informacje z sesji. Tutaj z pomocą przychodzi obserwator (obiekt Session), który monitoruje stan użytkownika (obiekt User) i aktualizuje dane sesji w momencie edycji użytkownika.
class Session implements SplObserver { public function update(SplSubject $subject) { $this->_updateSession($subject); } protected function _updateSession(User $user) { // zapisz nowe dane użytkownika do sesji } } class User implements SplSubject { protected $_observers = array(); public function editUser(array $data) { // dokonaj aktualizacji użytkownika (zapis do bazy, generowanie awatara, itd) // a nastepnie wyślij powiadmienie do sesji $this->notify(); } public function attach(SplObserver $observer) { $this->_observers[spl_object_hash($observer)] = $observer; } public function detach(SplObserver $observer) { unset($this->_observers[spl_object_hash($observer)]); } public function notify() { foreach($this->_observers as $observer) { $observer->update($this); } } } $user = new User(); $user->attach(new Session()); // za siedmioma klasami // za siedmioma obiektami $user->editUser(array( 'imie' => 'Jan', 'nazwisko' => 'Kowalski' ));
Powyższy przykład jest poprawny tylko w przypadku, gdy obserwatorem jest Session, a obserwowanym User. Jeśli chcielibyśmy aby Session obserwował więcej obiektów o różnych typach, należało by zmodyfikować metodę update tak, aby wykryć typ obiektu zgłaszającego aktualizację stanu i w zależności o niego wykonac stosowną akcję.
Podsumowanie
Wzorzec obserwatora doskonale sprawdza się wszędzie tam, gdzie stan jednego obiektu uzależniony jest od stanu drugiego obiektu. Dzięki temu, że obiekty nie są ze sobą ściśle powiązane, można je dowolnie rozbudowywać bez obawy, że wpłynie to negatywnie na proces obserwacji.
Autor wpisu: singles, dodany: 19.03.2011 23:06, tagi: php, zend_framework
Słowem wstępu – ZFQT – zapytacie pewnie co to znaczy? Nie, nikt nie implementuje Zend Framework’a korzystając bibliotek Qt – przynajmniej nic mi na ten temat nie wiadomo. Skrót ten oznacza Zend Framework Quick Tip. Tym wpisem chciałbym zacząć serię stosunkowo krótkich (postaram się:) notek zawierających porady/sztuczki w ZF. Niektóre mogą się wydawać oczywiste, ale nawet jeśli jedna osoba nauczy się czegoś nowego albo zrozumie dzięki temu, jak działa ZF, to uważam, że i tak warto prowadzić taką serię. Tym bardziej, że także komentarze często zawierają wartościową wiedzę. Tak więc czas zacząć!
Czym jest helper widoku?
View Helpers w ZF to klasy, które najczęściej implementują skomplikowaną logikę powiązaną z widokiem. Reference Guide od ZF opisuje je w ten sposób:
In your view scripts, often it is necessary to perform certain complex functions over and over: e.g., formatting a date, generating form elements, or displaying action links. You can use helper classes to perform these behaviors for you.
W ZF helpery definiujemy domyślnie jako klasy o nazwie pasującej do schematu Zend_View_Helper_[nazwaKlasy]
oraz zapisujemy w ścieżce application/views/helpers/
– oczywiście, nazwy klas helperów jak ich lokalizację można zmieniać. Helper musi posiadać metodę o nazwie takiej samej jak nazwa pliku, z tym że zaczynającą się od małej litery. Następnie możemy używać ich w plikach widoku *.phtml
poprzez $this->nazwaHelpera()
bądź w dowolnym miejscu aplikacji, gdzie mamy dostęp do instancji Zend_View
.
Przykład najprostszego helpera, który zwróci odpowiednią formę rzeczownika w zależności od przekazanych parametrów. Zaprogramowany dla większości przypadków języka angielskiego, ponieważ implementacja jest zdecydowanie prostsza, a chodzi o pokazanie zasady działania.
<?php // application/views/helpers/Pluralize.php class Zend_View_Helper_Pluralize { public function pluralize($noun, $number) { return $noun . ((intval($number) > 1) ? 's' : ''); } } // application/views/index/index.phtml <?php echo $this->pluralize('cat', 1); ?> // wyświetla 'cat' <?php echo $this->pluralize('cat', 4); ?> // wyświetla 'cats' <?php echo $this->pluralize('screen', 11); ?> // wyświetla 'screens'
Więcej informacji na temat helperów widoku, głównie tych standardowych znajdziecie w Reference Guide:View Helpers
Wiele metod dla jednego helpera
W momencie, kiedy wywołujecie nieistniejącą metodę obiektu widoku, Zend Framework automatycznie szuka klasy helpera pasującej do podanej nazwy. Jeśli takowa istnieje, tworzy obiekt tej klasy i wywołuje metodę zgodną z nazwą klasy – stąd wspomniane wcześniej wymaganie odnośnie nazywania metod. Rozwiązanie takie sprawdza się w większości przypadków.
Załóżmy jednak sytuację, że w naszej aplikacji musimy wyświetlać listę superbohaterów na dwa różne sposoby. Raz jako listę numerowaną, a drugi raz jako ciąg znaków oddzielony przecinkami. W drugim przypadku jednak imię i nazwisko bohatera musi być linkiem do strony wyświetlającej szczegóły na jego temat.
Pierwsza myśl niektórych programistów brzmi zazwyczaj:
no nic, muszę napisać dwie klasy – jedną do wyświetlania listy, drugą do linków oddzielonych przecinkami
Autor wpisu: Tomasz Kowalczyk, dodany: 19.03.2011 03:17, tagi: php
Autor wpisu: l3l0, dodany: 18.03.2011 23:07, tagi: php, symfony
Instalacja “Standard Symfony2″ ddistribution. Więcej informacji o instalacji na stronie symfony.com Teraz powinniśmy skonfigurować baze danych, możesz to sprawdzić na doctrine configuration
Konfiguracja naszego bundla
php app/console init:bundle "l3l0\BackendBundle" src
rejestrujemy “namespace” naszego “bundle” w app/autoload.php
$loader->registerNamespace(array( ... 'l3l0' => __DIR__ . '/../src', ... );
oraz rejestrujemy nasz “bundle” w app/AppKernel.php
public registerBundles() { $bundles = array( ... new l3l0\BackendBundle\l3l0BackendBundle() ); return $bundles; }
BDD Przy projekcie chcę używać BDD, chcę więc zainstalować Behata, żeby to zrobić starczy zainstalować BehatBundle. Opis instalacji na githubie https://github.com/Behat/BehatBundle/blob/master/README.md
Inicializacja Behata dla naszego bundla
php app/console behat:test:bundle --init l3l0\\BackendBundle
Implementacja historyjki użytkownika “Jako magazynier chcę logować się do systemu.”
Powiedzmy że musimy zaimpelementować taką funkcionalność.
Zaczniemy od pliku “feature” który jest odpowiednikiem testu w TDD może on wyglądać tak (eng) src/l3l0/BackendBundle/Tests/Features/login_to_system.feature
Feature: Login to system As a Guest I want to login to system. Background: Given user "l3l0" with password "password" is in database And I am on /login Scenario: Signin into system with valid login and password Given I fill in "loginForm[login]" with "l3l0" And I fill in "loginForm[password]" with "password" When I press "Login" in loginForm form Then I am logged in system And I should see "You are logged as l3l0" Scenario: Signin with invalid login Given I fill in "loginForm[login]" with "l3l01" And I fill in "loginForm[password]" with "password" When I press "Login" in loginForm form Then I am not logged in system And I should see "Your login or password is invalid" Scenario: Signin with invalid password Given I fill in "loginForm[login]" with "l3l0" And I fill in "loginForm[password]" with "password1" When I press "Login" in loginForm form Then I am not logged in system And I should see "Your login or password is invalid" Scenario: Signin with invalid login and password Given I fill in "loginForm[login]" with "l3l01" And I fill in "loginForm[password]" with "password1" When I press "Login" in loginForm form Then I am not logged in system And I should see "Your login or password is invalid"
Po uruchomienu feature zobaczymy że nie mamy zdefinowanych trzech kroków, aby je zdefinować musimy dodać do src/l3l0/BackendBundle/Tests/Features/steps/steps.php linijki:
Autor wpisu: matipl, dodany: 18.03.2011 09:33, tagi: php
Wczoraj zespół developerów wydał PHP w wersji 5.3.6. W głównej mierze dopracowano stabilność poprawiając ponad 60 błędów, w tym kilka bezpieczeństwa. Przypominam, że linia PHP 5.2 nie jest już rozwijana, ani wspierana i każdy powinien zaktualizować swoje środowisko do branchu 5.3.
Najważniejsze zmiany w PHP 5.3.6:
- poprawienie bezpieczeństwa w protokole fastcgi z FPM (problem z parsowaniem)
- poprawienie błędu bezpieczeństwa w parsowaniu w Phar
- poprawienie błędu związanego z przepełnieniem buforu dla wysokich wartości precyzji w konfiguracji ini
- zmieniono domyślną wartość serialize_precision ze 100 na 17
- aktualizacja wbudowanego Sqlite 3 do wersji 3.4.7
- aktualizacja PCRE do wersji 8.11
- dodano możliwość łączenia się HTTPS przez proxy z wykorzystaniem basic auth (stream_context/http/header/Proxy-Authorization)
- poprawienie błędu związanego z isset() i empty(), w pewnych wypadkach działanie kończyło się Runtime Error
Poprawki pojawiły się łącznie w ponad 20 modułach. Jest to pierwszy w tym roku tak duży zestaw poprawek.
Źródło: PHP
Autor wpisu: batman, dodany: 17.03.2011 08:00, tagi: php, zend_framework
Praca nad jednym z ostatnich projektów była swojego rodzaju powrotem do przeszłości. Wszystko za sprawą jedynego możliwego sposobu przeniesienia plików na serwer – kopiowanie z dysku lokalnego na serwer. Tak się złożyło, iż projekt ten stworzyłem w Zend Frameworku, który składa się z kilku tysięcy plików. Czas wgrywania plików – dobre kilkanaście minut. Jeśli dołożymy do tego zrywanie połączenia z serwerem do kilkaset plików okazuje się, że pół godziny marnuje się na “pilnowanie uploadu”. Zdeprymowany postępem uploadu zacząłem zastanawiać się nad lepszym rozwiązaniem i wpadłem na pomysł “skompilowania” aplikacji do pojedynczego pliku.
Dosyć szybko okazało, iż zadanie jakie przed sobą postawiłem, jest bardziej niż proste i sprowadza się do napisania trzech wierszy kodu PHP oraz niewielkiej modyfikacji struktury projektu.
Opisane poniżej rozwiązanie jest pomysłem, który wymaga dokładnego przetestowania. Dlatego też nie zalecam jego stosowania w środowiskach produkcyjnych.
Struktura katalogów
Struktura katalogów spakowanego projektu ogranicza się do katalogu public, na który wskazuje domena. Pozostałe katalogi projektu zostaną spakowane do archiwum phar i dołączone w pliku index.php. W zależności od konfiguracji serwera, archiwum phar będzie znajdować się w tym samym katalogu do plik index.php lub katalog wyżej (brak dostępu z poziomu przeglądarki).
Skrypt pakujący
Skrypt pakujący projekt do archiwum phar wygląda następująco.
$phar = new Phar('/sciezka/zapisu/phar_project.phar', null, 'phar_project.phar'); $phar->buildFromDirectory('/sciezka/do/projektu'); $phar->setStub( $phar->createDefaultStub('stub.php', 'stub.php') );
Te trzy wiersze kodu wystarczą do spakowania projektu. W pierwszym wierszu tworzymy obiekt pakujący. Jako pierwszy argument podajemy nazwę archiwum, drugim są flagi przekazane do klasy rodzica (RecursiveDirectoryIterator), ostatnim alias jakim będziemy się posługiwać podczas odwoływania się do archiwum.
W drugim wierszu wskazujemy katalog (projekt ZF), na podstawie którego utworzone zostanie archiwum. Metoda ta przyjmuje drugi parametr, będący wyrażeniem regularnym ograniczającym pliki dołączone do archiwum. Parametr ten jest opcjonalny.
Ostatni wiersz definiuje główny skrypt archiwum. Skrypt ten zostanie uruchomiony w momencie dołączenia pliku phar do skryptu. Pierwszym argumentem jest skrypt uruchamiany podczas korzystania a pliku phar w wierszu poleceń, drugim skrypt dla aplikacji webowej. Nic nie stoi na przeszkodzie aby był to ten sam plik.
index.php i stub.php
Ostatnia zmiana jaką należy wprowadzić wiąże się bezpośrednio ze zmianą struktury katalogów. Musimy wskazać w pliku index.php, że będziemy korzystać z archiwum phar.