Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Piotr Śliwa, dodany: 04.10.2010 19:11, tagi: php

Jakiś czas temu pracowałem nad projektem, którego funkcjonalności były oparte na wykorzystywaniu usług sieciowych o podobnej funkcjonalności. Pierwszym krokiem było ujednolicenie interfejsów tych usług. Na podstawie jednej z usług tworzyłem prosty interfejs, który później zaimplementowałem również dla pozostałych. Pierwszym krokiem było zastosowanie wzorca Facade, aby uprościć na potrzeby aplikacji interfejs pierwszej usługi sieciowej. Następnie pozostałe usługi sieciowe również zaadaptowałem do tego nowego interfejsu, wykorzystując wzorzec Adapter. Adapter od Facade różni się tym, że adapter przystosowuje zewnętrzny obiekt (zbiór obiektów) o podobnym interfejsie do już istniejącego, a fasada obudowuje złożony interfejs nowym, prostszym w użyciu. Ten wpis nie jest jednak o Adapterze (pisałem już o nim tutaj), ani o Facade, a o Service Stub.

Pewnego pięknego weekendu chciałem nadrobić zaległości budując kolejne funkcjonalności w serwisie wokół tych usług sieciowych. W tym projekcie w warstwie integracji z web services zastosowałem programowanie sterowane testami. Niestety w piątek po powrocie do domu, zastałem awarię internetu (która, jak się okazało, trwała do poniedziałku). Myślałem, że z moich planów nic nie wyniknie, ale postanowiłem skorzystać z wzorca Service Stub, czyli usługi zastępczej. Napisałem adapter usługi sieciowej, który przetrzymuje dane w pamięci i przechodzi wszystkie testy dla pierwszego z adapterów usług sieciowych. Dzięki temu mogłem pisać kolejne funkcjonalności serwisu, gdyż miałem adapter "zdalnej usługi", który działał zgodnie z założeniami.

Jaka jest istota Service Stub? Chodzi o zastąpienie zdalnej usługi sieciowej wywoływaniami lokalnymi, w celu przyspieszenia wykonywania testów jednostkowych. Jak widać na załączonym przykładzie, przyspieszenie wykonywania testów to nie jedyna korzyść z zastosowania tego wzorca.

Nie ma co się więcej rozpisywać o tym wzorcu, gdyż jest on bardzo prosty. Odsyłam do opisu Service Stub z katalogu Martina Fowlera oraz ciekawego artykułu Mocks Aren't Stubs, który pokazuje inne podejście do pisania testów, a mianowicie testowanie oparte na weryfikacji zachowania, a nie weryfikacji stanu.

Autor wpisu: nospor, dodany: 04.10.2010 10:33, tagi: php

Zainspirowany ostatnimi wypowiedziami początkujących (i nie tylko) programistów php, postanowiłem skrobnąć małe co nie co na temat błędów E_NOTICE. Niektórzy mogą mi zarzucić, że E_NOTICE to nie błąd, lecz ja słowo "błąd" użyłem celowo i z pełną świadomością. Zapewne bardziej fachowo należałoby napisać "komunikaty" E_NOTICE, aczkolwiek słowo komunikat to przecież komunikat więc można olać, a błąd to już coś poważniejszego, więc może by warto na niego zwrócić uwagę. Także by nie usypiać uwagi początkujących programistów (jak i niestestety tych już uważających się za niepoczątkujących) będę używał słowa "błąd".

Autor wpisu: sokzzuka, dodany: 03.10.2010 23:59, tagi: php

Zyx ma ciekawe przemyślenia, ja również je mam (tak mi się zdaje) więc tym razem moje RE, będzie dotyczyć jego artykułu pt. poeksperymentujmy z kontrolą uprawnień. Przedstawił on w swoim artykule sposób na weryfikacje czy dany kod ma dostęp do innego kodu na podstawie domen. Domena była obszarem gdzie obowiązywała dana polityka bezpieczeństwa, jeżeli zewnętrzny kod chciał na niej wykonać jakąś operacje, musiał wywołać metodę safeOperation z argumentami charakterystycznymi dla danej operacji i podać domenę z której pochodzi. Metoda safeOperation weryfikowała za pomocą policyManagera czy kod z innej domeny ma prawo wywołać daną operację i jeżeli ich nie posiadał to wyrzucany był wyjątek.

Rozwiązanie, które tu przedstawię jest w pewnym sensie generalizacją koncepcji Zyx-a. Na czym ono polega ?

Pierwsze założenie jest takie, że wszystkimi obiektami (a przynajmniej tymi które chcemy zabezpieczyć) zarządzą kontener IoC (inversion of control). Jest to maszynka, która tworzy nam i konfiguruje obiekty. Mój kontener IoC ma tą ciekawą właściwość, że pozwala zwracać zamiast żądanego obiektu jego proxy. Proxy jest obiektem przez którego przelatują wszystkie żądania do docelowego obiektu i posłuży on do kontroli uprawnień.

Cała idea leży w tym, że proxy przy próbie wywołania metody na obiekcie docelowym, sprawdzi w Acl, czy aktualny użytkownik (może być cokolwiek innego np wywołujący obiekt) ma uprawnienia do wywołania owej metody i jeżeli nie to rzuci wyjątkiem.

Żeby nie być gołosłownym, przedstawię fragmenty kodu jak to działa, na końcu artykułu będzie też link do kompletnego przykładu razem z moim kontenerem IoC.

Po pierwsze konfiguracja IoC (pełną informacje o konfiguracji znajdziesz w artykule o kontenerze IoC):

<object-config>
    <application>
        <!-- For now empty, objects declared here will be cached by APC or something similiar -->
    </application>
    <session>

    </session>
    <request>   

        <object class="Foo1">
            <property name="param2">other_baz</property>
            <property name="param1">baz</property>
            <proxy class="AccessProxy" />
        </object>
        <object class="AccessProxy">
            <property name="acl" type="class">Acl</property>
        </object>
        <object class="Acl">
            <property name="user" type="class">User</property>
        </object>

    </request>
</object-config>

I klasy biorące udział w eksperymencie: klasa docelowa:

class Foo1 {

    private $param1;
    private $param2;

    function __construct($param1, $param2){
        $this->param1 = $param1;
        $this->param2 = $param2;
    }

    function doHello(){
        echo 'hello <br/>';
    }

    function doFoo(){
        echo 'foo <br/>';
    }

}

Klasa Acl:

class Acl {

    protected $_user;
    public function __construct(User $user){
        $this->_user = $user;
    }

    public function isCallable($class, $method){

        if($this->_user->id == 10 and $method == 'doHello'){
            return true;
        }
        if($this->_user->id == 15 and $method == 'doFoo'){
            return true;
        }
        return false;
    }
}

Minimalistyczna klasa użytkownika ;)

class User {
    public $id = 10;
}

oraz najważniejsze – klasa proxy:

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

Autor wpisu: batman, dodany: 02.10.2010 21:00, tagi: zend_framework

Podczas pracy z Zend_Db_Table bardzo doskwierał mi brak metod, które automatycznie pobierałyby dane na podstawie nazwy metody. A co robi programista, któremu brakuje jakiejś funkcjonalności? Sam ją dopisuje. Po połączeniu tej funkcjonalności z automatycznym tworzeniem obiektu DbTable, powstał “magiczny model”.

Jeśli nie wiecie o czym piszę, za chwilę wszystko stanie się jasne. Załóżmy, że mamy w bazie danych tabelę users, o następującej strukturze (baza MySQL):

CREATE  TABLE IF NOT EXISTS `batman`.`users` (
  `iduser` INT NOT NULL AUTO_INCREMENT ,
  `login` VARCHAR(50) NULL ,
  `password` VARCHAR(32) NULL ,
  `firstname` VARCHAR(150) NULL ,
  `lastname` VARCHAR(250) NULL ,
  `email` VARCHAR(250) NULL ,
  PRIMARY KEY (`iduser`) )
ENGINE = InnoDB

Jeśli chcielibyśmy stworzyć metody zwracające kolekcje przefiltrowane według którejś z kolumn, musielibyśmy napisać szereg metod, np getUsersByLogin, czy getUsersByFirstname. Im więcej kolumn, po których chcielibyśmy zwracać dane, tym więcej czeka na pracy.

Z pomocą przychodzi magiczna metoda __call (stąd robocza nazwa “magiczny model”). Wystarczy, że w metodzie tej sprawdzimy dla jakiej kolumny chcemy pobierać dane. Resztą zajmie się “magia”.

public function __call($method, array $args)
{
    if(!isset($args[0])) {
        throw new Exception('You have to pass at least one argument in this magic');
    }

    $name = $this->getDbTable()->info(Zend_Db_Table_Abstract::NAME);
    $conditionCol = strtolower(
        str_ireplace('get' . $name . 'by', '', $method)
    );

    $cols = $this->getDbTable()->info(Zend_Db_Table_Abstract::COLS);
    if(!in_array($conditionCol, $cols)) {
        throw new Exception('What are you looking for? "' . $method . '" is invalid in this model');
    }

    $select = $this->getDbTable()->select();
    $select->where($conditionCol . ' = ?', $args[0]);

    return $this->getDbTable()->fetchAll($select);
}

Metoda najpierw sprawdza, czy przekazany został parametr, według którego będziemy pobierać dane. Następnie z nazwy metody usuwamy wszystkie zbędne elementy tak, by pozostała nam rzeczywista nazwa kolumny. Oczywiście musimy sprawdzić, czy wskazana kolumna znajduje się w odpytywanej tabeli. Na koniec wykorzystywana jest metoda fetchAll, do pobrania przefiltrowanych danych.

Wracając do naszej przykładowej tabeli. Jeśli powyższa metoda znajdzie się w modelu, wówczas zapis getUsersByFirstname(‘Anna’), spowoduje, że z tabeli pobrani zostaną wszyscy użytkownicy (a raczej użytkowniczki), których imię to Anna.

Pełny kod klasy wygląda następująco

abstract class Batman_Model
{
    /**
     * @var Zend_Db_Table_Abstract
     */
    protected $_dbTable = null;

    public function __construct()
    {
        $classParts = explode('_', get_class($this));
        $className = array_pop($classParts);
        array_push($classParts, 'DbTable', $className);
        $dbTableClassName = implode('_', $classParts);
        $this->_dbTable = new $dbTableClassName();
    }

    /**
     * @return Zend_Db_Table_Abstract
     */
    public function getDbTable()
    {
        if($this->_dbTable === null) {
            throw new Exception('There id no db table object');
        }
        return $this->_dbTable;
    }

    /**
     * @return Zend_Db_Table_Rowset_Abstract
     */
    public function __call($method, array $args)
    {
        if(!isset($args[0])) {
            throw new Exception('You have to pass at least one argument in this magic');
        }

        $name = $this->getDbTable()->info(Zend_Db_Table_Abstract::NAME);
        $conditionCol = strtolower(
            str_ireplace('get' . $name . 'by', '', $method)
        );

        $cols = $this->getDbTable()->info(Zend_Db_Table_Abstract::COLS);
        if(!in_array($conditionCol, $cols)) {
            throw new Exception('What are you looking for? "' . $method . '" is invalid in this model');
        }

        $select = $this->getDbTable()->select();
        $select->where($conditionCol . ' = ?', $args[0]);

        return $this->getDbTable()->fetchAll($select);
    }
}

Przykład użycia:

model i db table

class Application_Model_User extends Batman_Model
{
}

class Application_Model_DbTable_User extends Zend_Db_Table_Abstract
{
	protected $_name = 'users';
}

Kod w kontrolerze:

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

Autor wpisu: Blame, dodany: 01.10.2010 18:52, tagi: css

Tym razem pokażę wam, w jaki sposób wykorzystując sam CSS, wyclearować float’owane elementy bez dodawania zbędnych div’ów czy co tam jeszcze ktoś sobie wymyśli. Jedziemy :)

Przypuśćmy, że posiadamy następujący kod html:

<div style="float:left">Blok float'owany</div>
<div>Tego już nie chcemy opływać.</div>

Normalnie w takim przypadku większość programistów wstawiłaby po pierwszym bloku następny, ze stylem clear:both. Istnieje jednak łatwiejsza metoda.

W tym rozwiązaniu skorzystamy skorzystamy z pseudoklasy :after, żeby automatycznie wstawić za element z atrybutem float niewidoczną treść która nam wszystko ładnie wyclearuje.

Kod wygląda następująco:

<style type="text/css">

  .clear:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
    }

.clearfix {display: inline-block;}  /* hack dla IE pod Mac'iem */

</style>
<!--[if IE]>  /* Hack dla IE */
<style type="text/css">
  .clear {
    zoom: 1;
    display: block;
  }</style>
<![endif]-->

Teraz wystarczy do naszego div'a z ustawionym float dodać klasę .clear a powyższy kod doda za nim niewidzialną kropkę, która całkowicie rozwiąże problem zbędnych znaczników.
Jedynym minusem jest wątpliwa obsługa tego kodu pod starszymi przeglądarkami, nawet pomimo zastosowanych przeze mnie hacków. No ale jeśli cały czas będziemy się oglądać do tyłu to nie pójdziemy na przód, takie życie ;) 
Tagged: clear, clear:both, CSS, float
Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.