Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: eRIZ, dodany: 31.08.2010 23:41, tagi: internet

Dzisiaj (jeszcze ;)) trwa BlogDay, czyli wielka wymiana linków w blogosferze.

Zapraszam do przejrzenia moich typów. Niekoniecznie powiązanych z przewodnią tematyką mojego dziennika. ;)

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

Zend_Navigation jest pomocnym modułem Zend Framework, pozwalającym na prostą implementację menu oraz breadcrumbs na stronie opartej o ZF. Niestety nie jest to zbyt elastyczne narzędzie i nie pozwala “poszaleć”. Na szczęście mamy możliwość ingerencji w generowany kod HTML, a co za tym idzie, możemy dowolnie modyfikować sposób, w jaki Zend_Navigation generuje menu.

Na uwagę zasługują dwa rozwiązania. Każde z nich ma swoje wady i zalety i wybór któregoś z nich zależy wyłącznie od skomplikowania menu jakie mamy do stworzenia. W obu rozwiązaniach posłużę się następującym menu. Proszę zwrócić uwagę na niestandardowy element – image. Jest to ikonka, która wyświetli się zamiast etykiety. Brak tego elementu oznacza, że w menu wyświetli się tekst z elementu label.

Plik konfiguracyjny menu (application/configs/menu.php)

<?php
return array(
    array(
        'label' => 'Strona główna',
        'controller' => 'index',
        'action' => 'index',
        'image' => '/images/menu/home.png'
    ),
    array(
        'label' => 'O nas',
        'controller' => 'about',
        'action' => 'index'
    ),
    array(
        'label' => 'Kontakt',
        'controller' => 'contact',
        'action' => 'index'
    )
);

Metoda w pliku bootstrap.php

protected function _initNavigation()
{
	$this->bootstrap('view');
	$view = $this->getResource('view');
	$config = require APPLICATION_PATH . '/configs/menu.php';
	$navigation = new Zend_Navigation($config);
	$view->navigation($navigation);
}

Pierwsze rozwiązanie nie wymaga prawie żadnego wysiłku i sprowadza się do napisania prostego pliku z kodem HTML. Największą wadą takiego podejścia jest fakt, iż kolejne poziomy menu musimy sami oprogramować. Dlatego też najlepiej sprawdza się w przypadku prostych menu, gdzie mamy jeden lub określoną (niewielką) ilość poziomów. Użycie menu z przygotowanym plikiem wygląda następująco:

echo $this->navigation()->menu()->setPartial(array('menu.phtml', 'default'));

menu.phtml określa nazwę pliku jaki zostanie użyty do wyświetlenia menu, natomiast default określa w jakim module należy tego pliku szukać. Plik musi znajdować się w katalogu application/views/scripts. Przykładowa zawartość pliku menu.phtml

<ul>
	<?php foreach($this->container as $page): ?>
		<li<?php echo $page->isActive()?' class="active"':''; ?>>
			<a href="<?php echo $this->href; ?>">
				<?php if(isset($page->image)): ?>
					<img src="<?php echo $page->image; ?>"
						 alt="<?php echo $page->label; ?>" />
				<?php else: ?>
					<?php echo $page->label; ?>
				<?php endif; ?>
			</a>
		</li>
	<?php endforeach; ?>
</ul>

Do wszystkich elementów znajdujących się w menu możemy odwołać się poprzez właściwość obiektu $page. Niektóre właściwości, jak na przykład href, są zamieniane na metodę getHref. Możecie zajrzeć do klasy Zend_Navigation_Page oraz dziedziczących po niej Zend_Navigation_Page_Mvc oraz Zend_Navigation_Page_Uri w celu sprawdzenia jakie elementy i w jaki sposób można pobrać z obiektu $page.

Drugie podejście sprowadza się do napisania własnego helpera widoku, który dziedziczy po helperze Zend_View_Helper_Navigation_Menu oraz przesłania metodę htmlify, odpowiedzialną za wygenerowanie kodu HTML dla konkretnego elementu menu.

<?php
class Zend_View_Helper_ImgMenu extends Zend_View_Helper_Navigation_Menu
{
    public function imgMenu(Zend_Navigation_Container $container = null)
    {
        parent::menu($container);
        return $this;
    }

    public function htmlify(Zend_Navigation_Page $page)
    {
        // get label and title for translating
        $label = $page->getLabel();
        $title = $page->getTitle();

        // translate label and title?
        if ($this->getUseTranslator() && $t = $this->getTranslator()) {
            if (is_string($label) && !empty($label)) {
                $label = $t->translate($label);
            }
            if (is_string($title) && !empty($title)) {
                $title = $t->translate($title);
            }
        }

        // get attribs for element
        $attribs = array(
            'id'     => $page->getId(),
            'title'  => $title,
            'class'  => $page->getClass()
        );

        // does page have a href?
        if ($href = $page->getHref()) {
            $element = 'a';
            $attribs['href'] = $href;
            $attribs['target'] = $page->getTarget();
        } else {
            $element = 'span';
        }

        if(isset($page->image)) {
            $label = '<img src="' . $page->image . '" ?alt="' . $this- />view->escape($label) . '" . ?>';
        }
        else {
            $label = $this->view->escape($label);
        }

        return '<' . $element . $this->_htmlAttribs($attribs) . '>'
             . $label
             . '</' . ? $element>

Użycie menu z nowym helperem niczym się nie różni od użycia “zwykłego” menu. Zmienia się tylko nazwa helpera.

echo $this->navigation()->imgMenu();

Podejście drugie daje o wiele większe możliwości, ponieważ m.in. nie trzeba martwić się o kolejne poziomy menu – zadba o to Zend_Navigation. Wadą takiego podejścia jest potencjalne ryzyko zmiany helpera menu przez twórców frameworka.

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

Autor wpisu: sokzzuka, dodany: 29.08.2010 00:22, tagi: php

Dzisiejsze spotkanie z Zend’owym Front Controllerem i pluginami natchnęło mnie do kilku przemyśleń. Jednym z nich jest sposób jego działania. Aby w jakiś sposób ingerować w działanie FC należy albo napisać własną implementację jednej z koniecznych do życia przez FC klas (Dispatcher, Router etc) albo w przypadku mniejszych potrzeb stworzyć nowy plugin. Plugin’y są klasami, które reagują na określone zdarzenia typu preDispatch, postDispatch etc. Rozwiązanie to funkcjonuje i ma się dobrze. Wiem również ze słyszenia, że w innych frameworkach stosuje się podobne rozwiązania ale oparte np na sygnałach czy event handler’ach. Ja natomiast chciałbym przedstawić trochę alternatywne podejście do problemu oparte na wzorcu chain of responsibility. Czym jest ten wzorzec ? Generalnie jest on podobny do wzorca obserwator.

class ObserverOne implements SplObserver {
 
    public function update(SplSubject $subject) {
        echo $subject->getMessage().'One';
    }
 
}
 
class ObserverTwo implements SplObserver {
 
    public function update(SplSubject $subject) {
        echo $subject->getMessage().'Two';
    }
 
}
 
class Subject implements SplSubject {
    /**
     *
     * @var array<SplObserver> 
     */
    protected $_observers;
 
    public function attach(SplObserver $observer) {
        $this->_observers[] = $observer;
    }
 
    public function detach(SplObserver $observer) {
        $mKey = array_search($observer, $this->_observers);
        if($mKey !== false){
            unset($this->_observers[$mKey]);
        }
    }
 
    public function notify() {
        foreach($this->_observers as $observer){
            $observer->update($this);
        }
    }
 
    public function getMessage(){
        return 'Aloha !';
    }
 
}
 
$oSubject = new Subject();
$oSubject->attach(new ObserverOne());
$oSubject->attach(new ObserverTwo());
$oSubject->notify();
//Aloha!One Aloha!Two

Oba wzorce dają szanse kilku handler’om na obsłużenie jakiegoś zdarzenia. We wzorcu obserwatora po każdym wystąpieniu zdarzenia wszystkie zarejestrowane handler’y są powiadamiane równocześnie i decydują czy obsłużyć zdarzenie czy nie. Natomiast we wzorcu łańcucha odpowiedzialności powiadamiane są one w określonej kolejności. W klasycznej odmianie tego wzorca tylko jeden handler obsługuje event:

abstract class ChainElement {
 
    protected $_next;
 
    public function setNext(ChainElement $element) {
        $this->_next = $element;
        return $this;
    }
 
    public abstract function run($event);
}
 
class HandlerOne extends ChainElement {
 
    public function run($event) {
        if ($event == 1) {
            echo 'matched one!';
            return;
        }
        if (isset($this->_next)) {
            $this->_next->run($event);
        }
    }
 
}
 
class HandlerTwo extends ChainElement {
 
    public function run($event) {
        if ($event == 2) {
            echo 'matched two!';
            return;
        }
        if (isset($this->_next)) {
            $this->_next->run($event);
        }
    }
 
}
 
class HandlerThree extends ChainElement {
 
    public function run($event) {
        if ($event == 3) {
            echo 'matched three!';
            return;
        }
        if (isset($this->_next)) {
            $this->_next->run($event);
        }
    }
 
}
 
$oElement1 = new HandlerOne();
$oElement2 = new HandlerTwo();
$oElement3 = new HandlerThree();
 
$oElement1->setNext($oElement2);
$oElement2->setNext($oElement3);
 
$oElement1->run(3);
//matched three!

Generalnie można by powiedzieć, że klasyczny chain of responsibility zastępuje wyrażenie switch w sposób bardziej wyrafinowany. Pewnie już do tego momentu co uważniejsi z Was policzyli że użyłem słowa klasyczny co najmniej 2 razy. Oznacza to, że musi istnieć też wersja „nieklasyczna”. Jak już mówiłem wcześniej, w wersji klasycznej przechodzimy do następnego elementu łańcucha tylko kiedy aktualny stwierdza, że nie potrafi obsłużyć żądania, w innym przypadku łańcuch kończy się na aktualnym elemencie. Klasycznym przykładem takiej implementacji wzorca jest Zend_Router, w którym route’y można spinać w łańcuchy, dopasowanie trwa do pierwszego dobrego wzorca.

Są generalnie dwa alternatywne podejścia, pierwsze, różni się tym, że zawsze przelatywany jest cały łańcuch. Natomiast drugie, jest wariacją pierwszego i polega na przeniesieniu logiki sterowania łańcuchem na zewnątrz – zewnętrzny kod decyduje o przebiegu łańcucha.

Moje alternatywne podejście do FrontControllera będzie opierać się na tej drugiej wersji implementacji alternatywnej wzorca łańcucha odpowiedzialności. W podejściu tym będziemy mieli klasę RequestHandler:

class RequestHandler {
    /**
     *
     * @var array
     */
    protected $_chain;
 
    public function addElement(IChainElement $element) {
        $this->_chain[] = $element;
    }
 
    public function handle($request, $server) {
 
        if (empty($this->_chain)) {
            throw new Exception('No processing elements defined');
        }
 
        $aData = array(
            'request' => array(
                'input' => $request,
                'server' => $server
            ),
            'response' => array(
                'headers' => array(),
                'content' => ''
            )
        );
 
        foreach ($this->_chain as $element) {
            $aData = $element->process($aData);
        }
 
        return $aData['response'];
    }
}

Obiekt tej klasy jest nadzorcą łańcucha. Do dodawania elementów do łańcucha służy metoda addElement, która przyjmuje argumenty typu IChainElement, jest to interfejs, który muszą implementować wszystkie elementy łańcucha:

interface IChainElement {
    public function process($input);
}

Następnie gdy dodamy już elementy, wywołujemy metodę handle($request, $server). Jak się pewnie domyślacie $request zwykle będzie superglobalną zmienną $_REQUEST natomiast $server to $_SERVER. Dlaczego tak to zaimplementowałem zamiast po prostu używać tych zmiennych wewnątrz metody ? Zwiększa to testowalność tej klasy i czytelność kodu. Metoda handle przelatuje po wszystkich elementach łańcucha, które przetwarzają w kolejności dodania nasz ‘request’ generując ‘response’.

Dla przykładu konkretnego działania stworzyłem dwa elementy łańcucha, które są niezbędne by osiągnąć minimalną funkcjonalność Front Controller’a. Są to router:

class SampleRouter implements IChainElement {
 
    protected $_mapping;
 
    public function __construct() {
        $aMapping = array(
            'foo*' => 'foobar',
            'baz*' => 'foobaz'
        );
 
        $this->_mapping = $aMapping;
    }
 
    public function process($input) {
        $sHref = $input['request']['server']['REQUEST_URI'];
 
        foreach ($this->_mapping as $pattern => $viewId) {
            if (preg_match("/$pattern/i", $sHref)) {
                $input['request']['viewId'] = $viewId;
                return $input;
            }
        }
        $input['request']['viewId'] = '404';
        return $input;
    }
 
}

oraz Dispatcher:

class SampleDispatcher implements IChainElement {
 
    public function process($input) {
        //If the content exists - cache etc
        if(!empty($input['response']['content'])){
            return $input;
        }
 
        if($input['request']['viewId'] == '404'){
            $input['response']['headers'][] = 'HTTP/1.0 404 Not Found';
            $input['response']['content'] = 'Sorry page not found';
            return $input;
        }
 
        if($input['request']['viewId'] == 'foobar'){
            $input['response']['content'] = 'Foobar page!';
            return $input;
        }
 
        if($input['request']['viewId'] == 'foobaz'){
            $input['response']['content'] = 'FoobaZZZ page!';
            return $input;
        }
 
        return $input;
    }
 
}

Użycie:

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

Autor wpisu: batman, dodany: 28.08.2010 12:02, tagi: php

Filtry strumieni w PHP można porównać do filtrów znanych z Zend Framework. Są to klasy, które automatyzują czynności związane z odczytywaniem i zapisywaniem danych ze strumienia. Do jednego strumienia można dodać wiele filtrów, dzięki czemu programista zyskuje możliwość dowolnej modyfikacji strumienia.

Najprostszym i zarazem najlepszym przykładem zastosowania filtrów, będzie odczyt pliku i zmiana kodowania treści zawartych w pliku. W tym celu posłużę się plikiem CSV wygenerowanym z dokumentu Excel.

Typowy skrypt odczytujący dane z pliku wygląda następująco

$fp = fopen('./Book1.csv', 'r');
$output = '';
while(!feof($fp)) {
    $output .= fread($fp, 1024);
}
fclose($fp);
echo $output;

Jeśli dane w pliku nie spełniają naszych oczekiwań, wykonywane są operacje mające na celu dostosowanie ich do naszych wymagań. Sprowadza się to do wywołania funkcji zmieniającej kodowanie, zmieniającej wielkość liter, czy podmianie części danych. Jeśli zamiast tego zastosujemy filtr, odebrane dane będą od razu w pożądanej przez nas postaci.

Filtr jest klasą, która dziedziny po klasie php_user_filter. Klasa ta zawiera trzy właściwości oraz trzy metody.

Dostępne właściwości to:

  • filtername – nazwa filtru, który jest aktualnie wykorzystywany
  • params – zawiera parametry przekazane do filtra
  • stream – strumień, który aktualnie jest filtrowany

Dostęne metody:

  • onCreate – metoda wywołana przed rozpoczęciem filtrowania danych. Musi zwrócić true w przypadku poprawnego wykonania zawartych w swoim ciele operacji lub false jeśli operacje zakończyły się niepowodzeniem.
  • onClose – metoda wywołana po zakończeniu filtrowania.
  • filter – metoda odpowiedzialna za filtrowanie danych. Jest to jedyna etoda, która jest wymagana w klasie filtra. Przyjmuje cztery parametry:
    • $in – zasób wskazujący na bucket bridge, który zawiera kolejne porcje danych do filtrowania
    • $out – zasób wskazujący na bucket bridge do którego zostaną zapisane przefiltrowane dane
    • $consumed – parametr ten musi być przekazany przez referencję. Do jego wartości dodawana jest ilość aktualnie filtrowanych danych
    • $closing – parametr określający, że filtrowanie strumienia jest w ostatniej iteracji.

Metoda filter musi zwrócić jedną z trzech wartości:

  • PSFS_PASS_ON – filtrowanie zakończone sukcesem, na wyjście zwrócone zostały jakieś dane.
  • PSFS_FEED_ME – filtrowanie zakończone sukcesem, na wyjście nie zwrócono żadnych danych.
  • PSFS_ERR_FATAL – filtrowanie zakończone niepowodzeniem i nie może być kontynuowane.

Wykorzystując powyższe informacje, napiszemy klasę filtrującą, która zmieni kodowanie pliku na UTF.

class StreamFilterConvertEncoding extends php_user_filter
{
    public function filter($in, $out, &$consumed, $closing)
    {
        while($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = iconv('CP1250', 'UTF-8', $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

W powyższym kodzie tylko dwie funkcje mogą być niezrozumiałe. Pierwszą z nich jest stream_bucket_make_writeable. Zadaniem tej funkcji jest odebranie danych z wejścia i przekształcenie ich w bucket, na którym wykonywane są dalsze operacje. Drugą “egzotyczną” funkcją jest stream_bucket_append, która zapisuje dane z bucketa, do zmiennej wyjściowej. Zmienna $bucket jest obiektem klasy stdClass i zawiera zasób (właściwość bucket), dane (właściwość data) oraz wielkość odczytanych danych (właściwość datalen).

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

Autor wpisu: batman, dodany: 27.08.2010 07:00, tagi: php

Od samego początku swojego istnienia Bing (oraz usługi Windows Live) stawiał sobie za cel zdetronizowanie Google (przynajmniej na rynku wyszukiwarek). Wyszukiwarka, której funkcjonalności są coraz częściej kopiowane przez Google, zdobywa coraz większy rynek. Wraz z wyszukiwarką na podbój rynku ruszyły przeglądarkowy pakiet biurowy oraz nowa wersja poczty Hotmail.

Zdobywanie nowych użytkowników jest ważne dla rozwoju usług, jednak nie mniej ważne jest pozyskiwanie nowych programistów, dzięki którym będą powstawać kolejne aplikacje na bazie oferowanych usług (podobnie jak ma to miejsce w przypadku map Google). Ostatnio Microsoft “przypuścił atak” na społeczność PHP (wystarczy wspomnieć o wydaniu drugiej wersji sterownika SQL Server dla PHP).

Kolejnym krokiem do zjednania sobie społeczności PHP jest udostępnienie Bing Maps SDK. Programiści PHP zyskali możliwość korzystania z usług sieciowych REST i SOAP oraz z danych przestrzennych. Od teraz z poziomu PHP można między innymi wyszukiwać adresy, pobierać współrzędne geograficzne, a nawet wyświetlać mapę. Co więcej, programista ma możliwość dodawania markerów na mapie oraz wyznaczania tras dojazdu. Trasę taką można następnie nałożyć na mapę, np przy pomocy biblioteki GD (również z poziomu PHP).

Jedynym, dosyć poważnym w naszych realiach, minusem Bing Maps jest brak lokalizacji usługi. Dopóki nie będzie można wyszukiwać lokalnych adresów, Bing nie prześcignie Google.

Autor wpisu: Śpiechu, dodany: 26.08.2010 15:56, tagi: php

Siedzę sobie w domu na urlopie i uskuteczniam to, co lubię najbardziej czyli programuję. Niektórzy lubią wylegiwać się na plaży czekając aż czerniak łaskawie zawita na ich skórę, inni chodzą po zagranicznych targowiskach i pilnują portfela, a ja lubię siedzieć sobie wygodnie przed komputerem cały dzień i dłubać. Dzisiaj przedmiotem mojego dłubania jest implementacja kolejek [...]

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

Jednym z najczęstszych problemów związanych z Zend_Form jest brak możliwości dodawania kodu HTML między elementami formularza. Na szczęście Zend_Form jest na tyle rozbudowany, iż przy odrobinie pomysłowości wspomniany problem można dosyć łatwo obejść. Rozwiązań jest co najmniej kilka. Dzisiaj przedstawię dwa najprostsze.

Pierwszym rozwiązaniem jest zastosowanie opisu (description) w elemencie, po którym chcemy dodać kod HTML.

class Application_Form_Example1 extends Zend_Form
{
    public function init()
    {
        $this->setName('form-example1');

        $name = new Zend_Form_Element_Text('name');
        $name->setLabel('Nazwa');

        $email = new Zend_Form_Element_Text('email');
        $email->setLabel('E-mail')
              ->setDescription('<div id="some-id">lorem ippsum</div>');

        $phone = new Zend_Form_Element_Text('phone');
        $phone->setLabel('Telefon');

        $submit = new Zend_Form_Element_Submit('btn_save');
        $submit->setLabel('Zapisz');

        $this->addElement($name);
        $this->addElement($email);
        $this->addElement($phone);
        $this->addElement($submit);
    }
}

Kluczowym elementem powyższego kodu jest metoda setDescription, wywołana na obiekcie $email. Dzięki niej, do kodu formularza zostanie dodany kod HTML. Niestety kod zostanie wyświetlony w takiej formie, w jakiej został dodany – na stronie wyświetli się tekst

<div id="some-id">lorem ippsum</div>

Dzieje się tak dlatego, ponieważ kod HTML jest zamieniany na encje. Rozwiązaniem tego problemu jest ustawienie opcji escape dekoratora Description na false.

$email->addDecorator('Description', array('escape' => false));
Drugi sposób polega na zastosowaniu ukrytego elementu, w którym kod HTML wyświetlany jest w etykiecie.
class Application_Form_Example2 extends Zend_Form
{
    public function init()
    {
        $this->setName('form-example2');

        $name = new Zend_Form_Element_Text('name');
        $name->setLabel('Nazwa');

        $email = new Zend_Form_Element_Text('email');
        $email->setLabel('E-mail');

        $html = new Zend_Form_Element_Hidden('html');
        $html->setLabel('<div id="some-id">lorem ippsum</div>')
             ->setIgnore(true)
             ->addDecorator('Label', array('escape' => false));

        $phone = new Zend_Form_Element_Text('phone');
        $phone->setLabel('Telefon');

        $submit = new Zend_Form_Element_Submit('btn_save');
        $submit->setLabel('Zapisz');

        $this->addElement($name);
        $this->addElement($email);
        $this->addElement($html);
        $this->addElement($phone);
        $this->addElement($submit);
    }
}
Podobnie jak w przypadku opisu, etykieta również wymaga ustawienia opcji escape na false. Oczywistym jest, że podczas wysyłania formularza dodatkowe pole jest zbędne. Dzięki użyciu metody setIgnore, nadmiarowy element nie będzie występował w tablicy wartości przesłanych przez formularz.
Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.