Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM    Subskrybuj kanał ATOM dla tagu zend_framework Kanał ATOM (tag: zend_framework)

Autor wpisu: batman, dodany: 04.03.2011 08:00, tagi: zend_framework

Najpopularniejszym i póki co najskuteczniejszym zabezpieczeniem przed spamowaniem naszych formularzy jest captcha. Zend Framework, aspirujący do miana najkompletniejszej biblioteki dla języka PHP, dostarcza komponent Zend_Captcha, który w prosty sposób integruje się z formularzem. W kilku prostych krokach jesteśmy w stanie uodpornić nasz formularz na spamujące roboty.

Przed przystąpieniem do konfigurowania captchy, musimy uzbroić się w plik z czcionką, jaka zostanie użyta do utrudnienia życia spamerom. Czcionkę możemy znaleźć np. na stronie Google Web Fonts. Po pobraniu czcionki umieszczamy ją w projekcie. W moim przypadku jest to katalog data znajdujący się na tym samym poziomie co application. Następnie w publicznym katalogu musimy utworzyć katalog na wygenerowane pliki z kodem.

Nadeszła pora na skonfigurowanie captchy.

$captchaImage = new Zend_Captcha_Image();
$captchaImage->setFont(APPLICATION_PATH . '/../data/Kreon-Regular.ttf')
             ->setFontSize(36)
             ->setImgDir(APPLICATION_PATH . '/../public/captcha')
             ->setImgUrl('/captcha')
             ->setExpiration(90)
             ->setGcFreq(5)
             ->setWidth(200)
             ->setHeight(100)
             ->setTimeout(60)
             ->setWordlen(5);

Mimo iż powyższy kod do najpiękniejszych nie należy, doskonale spełnia swoje zadanie. Kolejne metody odpowiadają za:

  • setFont – ustawienie czcionki jaka zostanie użyta na obrazku
  • setFontSize – ustawienie rozmiaru czcionki
  • setImgDir – ustawienie katalogu, w którym będą przechowywane obrazki z kodem
  • setImgUrl – ścieżka do obrazków wstawiana do znacznika img
  • setExpiration – data ważności wygenerowanych plików
  • setGcFreq – ustawienie ilości requestów, po których usunięte zostaną nieaktualne obrazki z kodem
  • setWidth, setHeight – ustawienie szerokości i wysokości obrazka z kodem
  • setTimeout – ustawienie czasu ważności sesji dla aktualnego obrazka z kodem
  • setWordlen – ustawienie ilości liter wyświetlanych na obrazku

Po przebrnięciu przez gąszcz ustawień, możemy przejść do budowania formularza. W tym celu skorzystamy z klasy Zend_Form_Element_Captcha opakowującej stworzą wcześniej captchę w dekoratory formularza oraz ustawiająca odpowiedni walidator.

Całość wygląda następująco.

class Application_Form_Formularz extends Zend_Form
{
    public function init()
    {
        $captchaImage = new Zend_Captcha_Image();
        $captchaImage->setFont(APPLICATION_PATH . '/../data/Kreon-Regular.ttf')
                     ->setFontSize(36)
                     ->setImgDir(APPLICATION_PATH . '/../public/captcha')
                     ->setImgUrl('/captcha')
                     ->setExpiration(90)
                     ->setGcFreq(5)
                     ->setWidth(200)
                     ->setHeight(100)
                     ->setTimeout(60)
                     ->setWordlen(5);

        $captcha = new Zend_Form_Element_Captcha(
            'txt_captcha',
            array(
                'captcha' => $captchaImage
            )
        );

        $this->addElement($captcha);
    }
}

W celach prezentacyjnych ograniczyłem formularz do minimum.

Na koniec ważna uwaga. Jeśli będziecie chcieli modyfikować dekoratory, pamiętajcie, że Zend_Form_Element_Captcha nie korzysta z dekoratora ViewHelper. Zamiast niego należy użyć dekoratora Captcha.

Autor wpisu: batman, dodany: 02.03.2011 08:00, tagi: zend_framework

Kilka dni temu otrzymałem maila z zapytaniem dotyczącym helperów widoku w Zend Frameworku. Nie zagłębiając się w szczegóły, chodziło o mechanizm, który w zależności od pewnych warunków wstawi do layoutu stosowną treść. Podczas wymiany maili padło zdanie będące przyczynkiem dzisiejszego wpisu: nigdzie nie znalazłem konkretnego tutka nt view_helperów. Od dnia dzisiejszego powyższe stwierdzenie przestaje być prawdziwe.

Co to jest helper widoku?

Helperem widoku w kontekście Zend Frameworka nazywamy powtarzalny fragment kodu, który oprócz warstwy prezentacyjnej, posiada własną logikę. Za przykład niech posłuży typowy element większości stron – mały formularz logowania – idealne miejsce do szybkiego zalogowania się do serwisu. Ale co ma się stać z formularzem, gdy użytkownik jest już zalogowany? Najczęściej w jego miejscu pojawiają się link do konta użytkownika oraz link pozwalający na wylogowanie się z serwisu. W minimalistycznej wersji formularz po prostu znika. Oczywistym jest, że dodanie, nawet najprostszego, warunku przez osobę pracującą tylko z warstwą prezentacji może stanowić problem. Poza tym naszpikowanie layoutu dziesiątkami if-ów nie jest najlepszym pomysłem. Tutaj właśnie z pomocą przychodzą helpery widoku. Zamykają one problematycznego if-a w osobnym bycie i pozwalają skupić się na layoucie jako całości, a nie na sieczce instrukcji warunkowych.

Oczywiście helpery nie służą tylko jako pojemnik na instrukcje warunkowe. Każda złożona funkcjonalność, która wymaga wymieszania kodu PHP z HTML powinna znaleźć się helperze.

Jak tworzyć helpery widoku?

Helper widoku jest klasą dziedziczącą po klasie Zend_View_Helper_Abstract. Domyślną lokalizacją helperów jest katalog application/views/helpers. Nazwa klasy helpera musi składać się z przestrzeni nazw oraz nazwy helpera. W praktyce będzie to Zend_View_Helper_MojHelper, gdzie MojHelper jest nazwą helpera oraz nazwą pliku, w którym klasa musi zostać zapisana. Ostatnim wymogiem stawianym przed helperem widoku jest nazwa głównej metody. Musi się ona nazywać tak samo jak helper, przy czym ma rozpoczynać się małą literą oraz być metodą publiczną. Przykładowy helper będzie więc miał postać:

class Zend_View_Helper_MojHelper extends Zend_View_Helper_Abstract
{
    public function mojHelper()
    {
    }
}

Ważne jest aby pamiętać, że helper widoku ma zwrócić zawartość, a nie ją wyświetlić.

Do helpera można przekazywać argumenty. Nie różni się to niczym od przekazywania argumentów do standardowej metody znajdującej się w klasie.

class Zend_View_Helper_MojHelper extends Zend_View_Helper_Abstract
{
    public function mojHelper(array $arg1, $arg2 = null)
    {
    }
}

Jak wywoływać helpery widoku?

Z helperów korzysta się w taki sam sposób, jak ze zwykłych metod obiektu widoku. Uczulam tutaj na stwierdzenie “obiektu widoku”. Helpery powinny być wywoływane tylko i wyłącznie w plikach layoutu oraz widoku. To, że można je wywołać w innym miejscu, nie oznacza, że powinno się to robić.

Wywołanie przykładowego helpera będzie wyglądało następująco.

// wersja bez argumentów
echo $this->mojHelper();

// wersja z argumentami
echo $this->mojHelper(array('123', '234'), 'abc');

Zwracanie zawartości przez helpery

Wspomniałem, że helper nie powinien “echować” treści, tylko ją zwracać. Ale co zrobić w przypadku, gdy kod HTML zwracany przez helper jest sporych rozmiarów, przez co babranie się w escape’owanie (nigdy nie wiem jak to poprawnie zapisać – jakieś sugestie na przyszłość ?) znaczników i argumentów powoduje ból głowy?

Rozwiązania są dwa. Możemy zwrócić treść poprzez wyrenderowanie pliku widoku lub skorzystanie z partiala. Pierwszy sposób nie różni się niczym od standardowego renderowania widoku kontrolerze. Ewentualne zmienne zapisujemy do widoku, a następnie renderujemy plik widoku

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Śpiechu, dodany: 07.02.2011 21:41, tagi: mysql, php, zend_framework

Kontynuujemy temat zapytań z poprzedniej części. Punktem wyjścia będzie dla nas tablica wyników zapytania dorzuconego przeze mnie LEFT JOINA (w ramach aktualizacji wpisu na samym dole poprzedniej części). Aby nieco utrudnić dorzuciłem kolejnych dwóch powszechnie znanych idoli młodzieży: Antoniego Macierewicza i Stefana Niesiołowskiego, którzy dla odmiany nie będą mieli swoich ksywek. Ponadto dodałem drugą ksywkę towarzyszowi Tuskowi znanemu często na forach Onetu jako Rudy Oszust.

Reasumując: mamy polityka z dwoma ksywkami, jednego z jedną i dwóch bez ksywek. Dążymy do tego aby wyświetlić na liście rozwijanej formularza ich wszystkich. Zarówno oryginalne imię i nazwisko jak i ksywkę. Co jednak zrobić, skoro ksywka i oryginalne imię i nazwisko posiadają to samo id w bazie? Zamienimy je na unikatowe poprzez dodanie jakiegoś dodatku po oryginalnym id, np. 5–1, 5–2 itd.

Najpierw jednak stworzymy super prosty formularz zawierający pole wyboru typu select i przycisk zatwierdzający zmiany. Potrzebujemy klasy dziedziczącej po Zend_Form. Ja pracując w Zendzie zazwyczaj formularze wrzucam do katalogu forms równoległego do controllers, models itd.

class PolitycyForm extends Zend_Form {
 
   public function init() {
      $this->setMethod('post');
      // gdzie ma zostac wyslany formularz
      $this->setAction('/index/index');
 
      // tutaj wstawić zapytanie
      // z aktualizacji wpisu poprzedniej czesci
 
      $stmt = $select1->query();
      $rowset = $stmt->fetchAll();
 
      $wyniki = array();
      $counter = 1;
 
      foreach ($rowset as $row) {
         // tymczasowo kluczami staja sie wyniki zapytania,
         // a wartosciami id i kolejny numer
         $wyniki[$row['imie_nazwisko']] = $row['id'] . '-' . $counter++;
         // sprawdzamy czy ten ktos ma ksywke,
         // jezeli tak to dorzucamy do wynikow
         if ($row['ksywka'] != null) {
            $wyniki[$row['ksywka'] . ' (' . $row['imie_nazwisko'] . ')'] = $row['id']. '-' . $counter++;
         }
      }
      // nastepnie wszystko to sortujemy po kluczach
      // do poprawnego posortowania polskich znakow
      // uzywamy funkcji setlocale
      setlocale(LC_COLLATE, 'pl_PL.utf8');
      ksort($wyniki, SORT_LOCALE_STRING);
      // i zamieniamy miejscami klucze z wartosciami
      $wyniki = array_flip($wyniki);
 
      $formElement = new Zend_Form_Element_Select('politycy');
      $formElement->setRequired(true)
         // blokujemy tworzenie domyslnego walidatora
         // sprawdzajacego czy wynik jest w formie tablicy
         ->setRegisterInArrayValidator(false)
         ->setLabel('Wybierz swojego ulubionego polityka')
         ->setMultiOptions($wyniki)
         // sprawdzamy czy ktos nie robi psikusa
         ->addValidator(new Zend_Validate_Regex('/^[0-9]+\-[0-9]+$/'));   
      $this->addElement($formElement);
      // dodajemy pole typu submit
      $this->addElement('submit','wybierz');
   }   
}

Po wszystkich zabiegach tablica $wyniki przekazywana do obiektu Zend_Form_Element_Select posiada następującą strukturę:

array(7) {
  ["3-7"] => string(18) "Antoni Macierewicz"
  ["1-3"] => string(11) "Donald Tusk"
  ["2-6"] => string(31) "Jareczek (Jarosław Kaczyński)"
  ["2-5"] => string(20) "Jarosław Kaczyński"
  ["1-4"] => string(25) "Rudy Oszust (Donald Tusk)"
  ["1-2"] => string(27) "Słońce Peru (Donald Tusk)"
  ["4-8"] => string(20) "Stefan Niesiołowski"
}

Teraz pozostaje nam odebrać formularz. Żeby zbytnio nie komplikować dane odbierzemy w kontrolerze IndexController w akcji indexAction(). Na przykład tak:

$politycyForm = new PolitycyForm();
if ($this->_request->isPost()) {
   $dane = $this->getRequest()->getPost();
   if ($politycyForm->isValid($dane)) {
      // wyrzucamy szmelc po wlasciwym identyfikatorze
      $filtr = new Zend_Filter_PregReplace(
         array('match' => '/\-[0-9]+/',
               'replace' => ''));
      $przefiltrowane = $filtr->filter($dane['politycy']);
 
      // wykonujemy dzialania na bazie danych
      // co wykracza poza ramy tego wpisu
 
      // zakladamy, ze istenieje akcja panel-uzytkownika
      return $this->_redirect('/index/panel-uzytkownika');
   }
   else {
      // jezeli formularz nie przechodzi walidacji
      // to zostaje uzupelniony o wprowadzone poprzednio dane
      $politycyForm->populate($dane);
   }
}
// wyswietlamy formularz
$this->view->politycy = $politycyForm;

Możecie zapytać po co nam ten _redirect. Otóż zabezpiecza nas przed ponownym wyświetleniem użytkownikowi formularza i przed ewentualnym ponownym wysłaniem danych.

Jeżeli za szybko z czymś pojechałem, składać zażalenia w komentarzach :-)

Autor wpisu: sokzzuka, dodany: 05.02.2011 12:56, tagi: php, zend_framework

W dyskusji pod wpisem na blogu Matiego poruszyłem temat „walidatorów” w kontekście komponentu Zend Form w Zend Frameworku i tego, że mam zastrzeżenia co do ich koncepcji. Żeby być precyzyjnym nt. przedmiotu o którym będę się rozwodził pozwolę sobie po pierwsze odpowiedzieć na pytanie – czym jest walidacja ? Otóż słowo walidacja, jest spolszczeniem angielskiego czasownika „to validate”, który pochodzi od rzeczownika „valid”. „Valid” oznacza po prostu „poprawny”, a więc intuicyjnie „walidacja” to sprawdzanie poprawności. W przypadku oprogramowania walidacja, oznacza sprawdzenie poprawności danych w kontekście aplikacji, w której są one wykorzystane.

Najpowszechniejsze zastosowanie walidacji w aplikacjach opartych na ZF ma miejsce przy wszelkiego rodzaju formularzach. Zwykle w akcji kontrolera tworzony jest obiekt formularza potrzebnej klasy, w którym do kolejnych jego elementów „przypięte” są walidatory. Gdy wystąpi interesujące nas zdarzenie, zwykle jest nim wysłanie formularza metodą „POST”, następuje przekazanie danych do formularza, który sprawdza ich poprawność. Następnie dane są z niego wyciągane i przekazywane do jakiegoś modelu, który już zajmuje się nimi dalej.

Taki typowy flow prezentuje poniższy kod:

class Article_Form extends Zend_Form {

	public function init(){

		$oTitle = new Zend_Form_Element_Text('title');
		$oTitle->addValidator(new Zend_Validator_NotEmpty);

		$this->addElement($oTitle);
//...inne elementy
	}

}

class ArticleController extends Zend_Controller_Action {

	public function createAction(){

		$oForm = new Article_Form();
		if($this->_request->isPost()){
				$aPost = $this->_request->getPost();
				if($oForm->isValid($aPost)){
					$oArticleTable = new Article_Table;
					$oArticleTable->insert($oForm->getValues());

					$this->_redirect('/somepage');
				}

		}
		$this->view->form = $oForm;
	}

//... inne metody

}

Rozwiązanie to świetnie się sprawdza, gdy całe działanie naszej strony sprowadza się do operacji typu CRUD (Create Retrieve Update Delete). Typowym przykładem takiej aplikacji jest dowolny blog, czy prosty firmowy CMS.

Problem z tego typu walidacją pojawia się wtedy, kiedy nasza aplikacja zaczyna robić się skomplikowana. Klient potrzebuje zaimplementować złożona logikę biznesową i działanie naszego softu nie sprowadza się już do przeprowadzania podstawowych operacji na bazie danych. Zadania jakie są przed nim postawione wymagają dogłębnej znajomości procesów biznesowych klienta.

Uczestniczyłem w takim projekcie (ecommerce) dla dużego hurtowego dostawcy artykułów papierniczych. Możecie mi wierzyć albo i nie, ale zamówienie długopisu albo żółtych samoprzylepnych karteczek może być bardzo skomplikowanym procesem, w którym zaangażowane jest kilka osób/użytkowników systemu.

Wróćmy jednak do meritum, czyli naszych walidatorów. Co jest nie tak z kodem, który został zaprezentowany wcześniej ? Otóż wraz ze wzrostem złożoności warunków, które spełniać ma flow naszego kodu, rośnie jego zależność od kontekstu w jakim zostaje wykonywany. Niestety okazuje się, że walidatory, którymi możemy sprawdzać poprawność pojedynczego pola przestają wystarczyć. Oczywiście można napisać taki walidator, który sprawdza poprawność pola w kontekście innych pól naszego formularza. Można też napisać walidator, który sięgnie do bazy danych by sprawdzić jakieś dodatkowe informacje. Zawsze jednak koniec końców, kończymy z wielką zagmatwaną siecią if-ów.

Stosując taką strategię walidacji dorobimy się całego wianuszka walidatorów albo też alternatywnie zaczniemy przenosić część walidacji do akcji kontrolera. Jeżeli jeszcze dodatkowo chcemy wykorzystać dany formularz w innym miejscu systemu, gdzie wygląd formularza jest taki sam, natomiast kontekst jest trochę inny, dołożymy kolejne „ify” i skończymy z nieczytelnym i nierozwiązywalnym węzłem gordyjskim.

Raz już spotkałem się z taką sytuacją i jako, że pośrednio sam się do niej przyczyniłem, postanowiłem poszukać jakiegoś rozwiązania, które pozwoli mi uniknąć takiej sytuacji w przyszłości. Po wielu poszukiwaniach w internecie trafiłem na metodykę DDD (Domain Driven Design).  DDD proponuje rozwiązanie opcji walidacji w bardzo elegancki sposób.

Całą idee przestawię prosto na przykładzie aplikacji do rejestrowania pacjentów w placówkach polskiej tzw. „służby zdrowia”.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Śpiechu, dodany: 24.01.2011 20:10, tagi: mysql, php, zend_framework

Ostatnio w ramach sportów wyczynowych majstruję trochę z zapytaniami do bazy danych w Zendzie. Sprawdzam co można wycisnąć z obiektów Zend_Db_Select. Ten wpis zdecydowałem się podzielić na dwie części. W pierwszej pokażę jak wygenerować dosyć złożone zapytanie do bazy danych za pomocą kilku obiektów Zend_Db_Select, a w drugiej zajmiemy się stworzeniem formularza i odebraniem danych.

Naszym celem będzie utworzenie alfabetycznej listy polityków, których wszyscy lubimy wraz z ich powszechnie znanymi pseudonimami, np.

<select>
<option value="1">Donald Tusk</option>
<option value="2">Jareczek (Jarosław Kaczyński)</option>
<option value="3">Jarosław Kaczyński</option>
<option value="4">Słońce Peru (Donald Tusk)</option>
</select>

Najpierw musimy mieć skąd brać naszych wybrańców narodu. Stworzymy sobie bazę danych mniej więcej taką: Obrazek bazy danych polityków zrobiłem fajnym narzędziem online WWW SQL Designer. Schemat jest oczywiście maksymalnie uproszczony. Nie czepiać się, że imiona i nazwiska trzymam w jednym polu. Chodzi nam o relację jeden polityk ma wiele pseudonimów. Macie poniżej trochę kodu SQL wyeksportowanego przez phpMyAdmin plus kilka przykładowych wartości.

CREATE TABLE IF NOT EXISTS `politycy` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `imie_nazwisko` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='tabela zawierajaca imiona i nazwiska politykow' AUTO_INCREMENT=3 ;
 
INSERT INTO `politycy` (`id`, `imie_nazwisko`) VALUES
(1, 'Donald Tusk'),
(2, 'Jarosław Kaczyński');
 
CREATE TABLE IF NOT EXISTS `politycy_ksywki` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ksywka` varchar(20) NOT NULL,
  `polityk_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `polityk_id` (`polityk_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='zawiera popularne pseudonimy politykow' AUTO_INCREMENT=3 ;
 
INSERT INTO `politycy_ksywki` (`id`, `ksywka`, `polityk_id`) VALUES
(1, 'Słońce Peru', 1),
(2, 'Jareczek', 2);
 
ALTER TABLE `politycy_ksywki`
  ADD CONSTRAINT `politycy_ksywki_ibfk_1` FOREIGN KEY (`polityk_id`) REFERENCES `politycy` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

Następnie spróbujemy sobie wygenerować dosyć spore zapytanie SQL, które za jednym zamachem wypisze wszystkich polityków, wszystkie pseudonimy i dodatkowo w nawiasie wypisze którego polityka który pseudonim dotyczy. Wszystko oczywiście w Zend Framework.

// adapter bazy umieszczony w rejestrze
$dbAdapter = Zend_Registry::get('db');
$select1 = $dbAdapter->select()
   // podstawowa tabela z politykami
   ->from(array('p' => 'politycy'),
          // ktore pola (lacznie z aliasem)
          array('p.id','p.imie_nazwisko'));
// podzapytanie dla ksywek
$subSelect = $dbAdapter->select()  
   ->from(array('pp' => 'politycy'),
          array('pp.imie_nazwisko'))
   // warunek dla podzapytania
   ->where('pp.id = pk.polityk_id')
   // powinno przyspieszyc duze bazy
   ->limit(1);
$select2 = $dbAdapter->select()
   // tabela z ksywkami
   ->from(array('pk' => 'politycy_ksywki'),
          array('pk.polityk_id', 
                // Zend_Db_Expr konieczny przy wywolywaniu funkcji SQL
                new Zend_Db_Expr("CONCAT(pk.ksywka, ' (', ({$subSelect}) , ')')")));
$selectUnion = $dbAdaptery->select()
   // konstruujemy zapytanie typu union
   ->union(array($select1, $select2))
   // szeregujemy wyniki
   ->order('imie_nazwisko ASC');
$stmt = $select->query();
$rowset = $stmt->fetchAll();

Powyższy kod stworzył niczego sobie zapytanie:

SELECT `p`.`id`, `p`.`imie_nazwisko` FROM `politycy` AS `p` UNION SELECT `pk`.`polityk_id`, CONCAT(pk.ksywka, ' (', (SELECT `pp`.`imie_nazwisko` FROM `politycy` AS `pp` WHERE (pp.id = pk.polityk_id) LIMIT 1) , ')') FROM `politycy_ksywki` AS `pk` ORDER BY `imie_nazwisko` ASC

Zmienna $rowset zawiera wynik zapytania w postaci tablic:

array(4) {
  [0] => array(2) {
    ["id"] => string(1) "1"
    ["imie_nazwisko"] => string(11) "Donald Tusk"
  }
  [1] => array(2) {
    ["id"] => string(1) "2"
    ["imie_nazwisko"] => string(31) "Jareczek (Jarosław Kaczyński)"
  }
  [2] => array(2) {
    ["id"] => string(1) "2"
    ["imie_nazwisko"] => string(20) "Jarosław Kaczyński"
  }
  [3] => array(2) {
    ["id"] => string(1) "1"
    ["imie_nazwisko"] => string(27) "Słońce Peru (Donald Tusk)"
  }
}

Na koniec uwaga: obiekty typu Zend_Db_Select są przydatne tylko wtedy, gdy nasze zapytanie nie ma charakteru stałego, tzn. różne czynniki wpływają na jego kształt, przez co musi być tworzone w locie. Jeżeli wiemy, że zapytanie zawsze będzie takie samo to jest to zwykłe marnotrawstwo zasobów serwera, aczkolwiek ładnie wygląda i szybko się pisze.

Autor wpisu: Śpiechu, dodany: 26.12.2010 00:27, tagi: zend_framework, php

Mamy święta i dużo wolnego czasu, dlatego dzisiaj będziemy zajmować się modułem Zend Framework odpowiedzialnym za czas – Zend_Date. Proponuję przeczytać moje gryzmoły zamiast objadać się nadmiernie karpiem czy co tam macie. Dodatkowo zahaczymy o Zend_Config w postaci odczytu, modyfikacji i zapisu nowych danych do pliku .ini.

Będziemy robić moduł odpowiedzialny za automatyczne usuwanie dawno otwartych (a zatem niepotrzebnych) jakichś plików na serwerze. Mogłyby to być pliki miniaturek obrazków, które kiedyś nam system wygenerował i są już niepotrzebne.

Domyślnie każda aplikacja w ZF ma plik konfiguracyjny application.ini zapisany w /application/configs/. Dla naszych celów założymy sobie plik thumbs.ini, który będziemy mordować czytaj: ciągle nadpisywać. W pliku zapiszemy sobie 4 linijki:

[production]
cleanafter = "Dec 25, 2010 6:26:09 PM"
checkinterval = "5"
intervalunit = "MINUTE"

Wyraz w nawiasie oznacza środowisko, w którym pracujemy, cleanafter to data zapisana w formacie DATETIME. Checkinterval i intervalunit to zmienne służące nam do wygenerowania nowej daty cleanafter.

Kod, który podam poniżej można sobie zapisać w jakimś kontrolerze albo jako plugin.

// ważne jest ustawienie opcji 'allowModifications' na true,
// bo inaczej konfiguracja będzie w trybie tylko do odczytu
$config = new Zend_Config_Ini(ROOT_DIR.'/application/configs/thumbs.ini','production',
   array('allowModifications' => true));
// konstruujemy obiekt Zend_Date na podstawie danych z
// konfiguracji i podajemy format, w jakim dostarczymy dane nt. daty
$cleanAfter = new Zend_Date($config->cleanafter, Zend_Date::DATETIME);
// jeżeli nic nie podamy to Zend weźmie sobie datę i czas obecny
$currentDate = new Zend_Date();
// bardzo fajna funkcja isEarlier() porównująca daty
if ($cleanAfter->isEarlier($currentDate)) {
   echo 'coś robię';
   // wyznaczamy nową datę do wpisania do konfiguracji;
   // myślę, że sklonowanie obiektu daty powoduje trochę mniejszy
   // narzut zasobów niż tworzenie od nowa obiektu
   $newCleanDate = clone $currentDate;
   // dodajemy datę; funkcja add() ma 2 argumenty: ile dodać i co dodać;
   $newCleanDate->add(
      $config->checkinterval,
      constant('Zend_Date::' . $config->intervalunit)
      );
   // przypisujemy do configa datę w formacie DATETIME
   $config->cleanafter = $newCleanDate->get(Zend_Date::DATETIME);
   // tworzymy obiekt służący do zapisu configów
   $writer = new Zend_Config_Writer_Ini();
   $writer->setConfig($config);
   $writer->setFilename(ROOT_DIR.'/application/configs/thumbs.ini');
   $writer->write();
 
   // przygotowujemy obiekt daty, z którym będziemy
   // porównywać czas ostatniego otwarcia plików
   $deleteOlderThan = clone $currentDate;
   // add() dodawało, sub() odejmuje; my odejmiemy 1 tydzień
   $deleteOlderThan->sub(1, Zend_Date::WEEK);
   // tworzymy iterator podając katalog z miniaturkami do sprawdzenia
   $dirIterator = new DirectoryIterator(APPLICATION_PATH  . '/../public/thumbs');
   foreach ($dirIterator as $file) {
      // $getATime() podaje czas ostatniego otwarcia pliku
      // w formacie TIMESTAMP, z którego skonstruujemy datę
      $fileOpenDate = new Zend_Date($file->getATime(), Zend_Date::TIMESTAMP);
      // jeżeli data ostatniego otwarcia jest wcześniejsza
      // niż przyjęty przez nas limit 1 tygodnia to wywalamy plik
      // wygłuszając ewentualne komunikaty poprzez @
      if ($fileOpenDate->isEarlier($deleteOlderThan)) {
         @unlink($file->getRealPath());
      }
   }
}

Polecam zwrócić uwagę na constant(), który w locie na podstawie nazwy odwoła się do stałej obiektu. Konstrukcja typu ${'jakas' . $zmienna} prawidłowa dla zmiennych nie jest dostępna dla stałych i należy użyć funkcji constant().

A poza tym to Wesołych Świąt :-D

Autor wpisu: batman, dodany: 13.12.2010 08:00, tagi: zend_framework

Jednym z ostatnich etapów serii PHP w chmurze będzie uruchomienie aplikacji opartej o Zend Framework. Ponieważ aplikacje PHP hostowane na Windows Azure uruchamiane są w ramach serwera IIS, postanowiłem już zawczasu sprawdzić czy IIS nie będzie sprawiał problemów. Wynik testów przerósł moje najśmielsze oczekiwania. Okazało się, że IIS oferuje szereg usprawnień, które znacząco poprawiają komfort pracy z aplikacją.

Pierwszą czynnością jaką należy wykonać jest upewnienie się, czy na serwerze IIS działa PHP (dokładny opis instalacji PHP na IIS znajdziecie tutaj). Jeśli wszystko działa, można przejść dalej, czyli do utworzenia aplikacji ZF. Czynność tą wykonuje się identycznie jak w przypadku serwera Apache. Podczas korzystania z Apache dla każdej aplikacji tworzyłem wirtualnego hosta i przypisywałem do niego domenę. Postanowiłem nie zmieniać przyzwyczajeń i przenieść ten sposób na IIS. Okazało się to prostsze niż myślałem. W celu stworzenia “wirtualnego hosta”, należy otworzyć menedżera IIS, kliknąć prawym przyciskiem myszy na katalog Witryny i z menu wybrać opcję Dodaj witrynę sieci Web… Spowoduje to otwarcie okna dialogowego takiego jak na poniższym rysunku.

dodawanie-witryny

W oknie tym musimy wypełnić trzy pola:

  • nazwa witryny – nazwa pod jaką będzie wyświetlana nasza witryna w menedżerze IIS
  • ścieżka fizyczna – ścieżka do katalogu, w którym znajduje się plik index.php (domyślnie jest to katalog public)
  • nazwa hosta – host pod jakim nasza aplikacja będzie dostępna. Nie zapomnijcie dodać tej nazwy do pliku hosts

Pozostałe pola nie są nam do niczego potrzebne, więc zostawiamy je bez zmian.

Aplikację można już przetestować. Wpisanie w przeglądarkę nazwy hosta przypisanego do witryny, spowoduje wyświetlenie głównej strony naszej aplikacji. Niestety nic więcej na tym etapie nie będziemy w stanie uzyskać. Należy pamiętać, że współczesne frameworki PHP (ZF nie jest tutaj wyjątkiem) przekierowują cały ruch (za wyjątkiem requestów do istniejących zasobów) na plik index.php. Do tego celu wykorzystywany jest moduł mod_rewrite i plik .htaccess. Na szczęście IIS również posiada możliwość z przepisywania adresów. Służy do tego moduł URL Rewrite, który niewiele się różni od swojego apache’owego odpowiednika. Doinstalowanie tego modułu do IIS jest tak samo proste (a nawet prostsze) jak instalacja PHP i polega na zaznaczeniu odpowiedniego checkboxa w Web Platform Installer. Więcej informacji na temat URL Rewrite znajdziecie na stronie modułu (za jakiś czas popełnię wpis na ten temat).

Po zainstalowaniu URL Rewrite wystarczy utworzyć plik web.config w katalogu głównym aplikacji (domyślnie jest to public) i uzupełnić jego treść:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<system.webServer>
		<rewrite>
			<rules>
				<rule name="Fizyczne zasoby" stopProcessing="true">
					<match url="^.*$" />
					<conditions logicalGrouping="MatchAny">
						<add input="{REQUEST_FILENAME}" matchType="IsFile" pattern="" ignoreCase="false" />
						<add input="{REQUEST_FILENAME}" matchType="IsDirectory" pattern="" ignoreCase="false" />
					</conditions>
					<action type="None" />
				</rule>
				<rule name="Rewrite" stopProcessing="true">
					<match url="^.*$" />
					<action type="Rewrite" url="index.php" />
				</rule>
			</rules>
		</rewrite>
	</system.webServer>
</configuration>

Oczywiście powyższe reguły można wyklikać, więc dla każdego coś miłego.

W tym momencie mały w pełni działającą aplikację opartą o Zend Framework, stojącą na serwerze IIS. Nie miałem jeszcze okazji dokładnego przetestowania takiej konfiguracji, jednak nic nie wskazuje na to, aby pojawiły się jakieś problemy.

Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.