Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Diabl0, dodany: 05.07.2012 01:07, tagi: zend_framework, php

Tym razem poruszę jednocześnie dwie kwestie – tworzenie i obsługę webservices oraz zarządzanie uprawnieniami.

Zanim zaczniemy z autoryzacją i prawami dostępu musimy mieć usługi które udostępniamy. Nie wiem czy wszyscy zdają sobie sprawę że z pomocą ZF w miarę prosto i szybko można za jednym zamachem stworzyć usługi obsługiwane za pomocą trzech najbardziej obecnie popularnych protokołów: XML-RPC, Json-RPC i SOAP.

Stworzymy sobie kontroler do obsługi usług (w moim przypadku main_ServicesController) a w nim 3 metody – każda dla osobnego protokołu.

class main_ServicesController extends Mao_Controller_Action {
	/**
	 *
	 * Server JSON-RPC
	 */
	public function jsonAction()
	{
		$this->disableLayoutAndViews();

		$server = new Zend_Json_Server();

		$this->_setClasses($server);

		if ('GET' == $_SERVER['REQUEST_METHOD']) {
			$server
			->setTarget( 'http://' . $_SERVER['HTTP_HOST'] . '' . $this->view->url(array('module' => 'main', 'controller' => 'services', 'action' => 'json' )) )
			->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);
			$smd = $server->getServiceMap();

			header('Content-Type: application/json');
			echo $smd;
			return;
		}

		echo $server->handle();
	}

	/**
	 *
	 * Server XML-RPC
	 */
	public function xmlAction()
	{
		$this->disableLayoutAndViews();

		Zend_XmlRpc_Value::setGenerator(new Zend_XmlRpc_Generator_XmlWriter());

	    // Specify a cache file
	    $cacheFile = APPLICATION_PATH . '/temp/xmlrpc.cache';

		$server = new Zend_XmlRpc_Server();

	    // Attempt to retrieve server definition from cache
	    if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
	        $this->_setClasses($server);

	        // Save cache
	        // @todo: uncomment when going live
	        //Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
	    }

		echo $server->handle();

	}

	/**
	 *
	 * Server SOAP
	 */
	public function soapAction() {
		$this->disableLayoutAndViews();

		$serviceURL = $url = 'http://' . $_SERVER['HTTP_HOST'] . '' . $this->view->url(array('module' => 'main', 'controller' => 'services', 'action' => 'soap' ));

		// Generate WSDL relevant to code
		if (isset($_GET['wsdl'])){
		    $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex');
		    $this->_setClasses($autodiscover);
		    $autodiscover->handle();
		} else {
		    $server = new Zend_Soap_Server($serviceURL . "?wsdl");
		    $this->_setClasses($server);
		    $server->handle();
		}
	}

    /**
     * Sets classes for servers
     *
     * @param object $server
     */
	protected function _setClasses( $server ) {

		if ( method_exists( $server, 'setClass' ) ) {
			$server->setClass( 'main_services_Auth', 'auth' );
			$server->setClass( 'shop_services_Products', 'products' );
			$server->setClass( 'shop_services_Orders', 'orders' );
		}
	}

}

Jeden kontroller, 3 akcje, 3 protokoły.

Skoro mamy już zdefiniowane usługi to czas zająć się autoryzacją i uprawnieniami.

Niestety żaden z protokołów nie ma odgórnie ustalonych zasad realizacji tego problemu i w sieci można się spotkać z wieloma sposobami. Ja zamiast każdorazowe przekazywanie pełnych danych logowania zdecydowałem się na tzw. tokeny. Jedna metoda służy do uwierzytelnienia i wystawienia tokena który natomiast jest przekazywany przy wywoływaniu kolejnych metod.

Za logowanie odpowiada klasa:


class main_services_Auth extends main_services_Abstract {

	/**
	 *
	 * Login and generate login token.
	 *
	 * @param string $username username
	 * @param string $password password
	 * @param array $additional	optional additional informations (currently unused)
	 *
	 * @return array dod
	 */
	public function login( $username, $password, $additional = array() ) {

		// 1 - sprawdzamy usera i hasło
		$db = Zend_Registry::get ( 'db' );

		$authAdapter = new Zend_Auth_Adapter_DbTable ( $db );
		$authAdapter->setTableName ( 'users' );
		$authAdapter->setIdentityColumn ( 'username' );
		$authAdapter->setCredentialColumn ( 'password' );
		$authAdapter->setCredentialTreatment('MD5(?) AND active = 1');

		// Set the input credential values to authenticate against
		$authAdapter->setIdentity ( $username );
		$authAdapter->setCredential ( $password );

		$auth = Zend_Auth::getInstance ();
		$result = $auth->authenticate ( $authAdapter );

		if ($result->isValid ()) {
			//2 - sprawdzamy/generujemy token

			$tokensModel = new main_models_Services_LoginTokens();
			$tokenRow = $tokensModel->fetchTokenFor( $username, true );

			return array('status' => true, 'token' => $tokenRow->token);
		}

		return array('status' => false, 'error' => 'Unable to login');
	}
}

Jak widać dość typowe zendowska autoryzacja, jedyna różnica to to że po zalogowaniu nie zapisujemy danych użytkownika do Zend_Auth_Storate a jedynie zwracamy token logowania.

Samo generowanie tokena to:

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

Autor wpisu: Łukasz Socha, dodany: 03.07.2012 19:33, tagi: php

pobierz w .pdf(przeznaczone do wydruku)

Trzecia część z serii wpisów o wzorcach projektowych. Tym razem omówię wzorzec projektowy Prototyp (Prototype).

 

Prototyp jest wzorcem, opisującym mechanizm tworzenie nowych obiektów poprzez klonowanie jednego obiektu macierzystego. Mechanizm klonowania wykorzystywany jest wówczas, gdy należy wykreować dużą liczbę obiektów tego samego typu lub istnieje potrzeba tworzenia zbioru obiektów o bardzo podobnych właściwościach.

Diagram klas wzorca Prototype

Implementując ten wzorzec deklaruje się klasę Prototype z abstrakcyjną operacją klonującą clone(). Operacja ta jest implementowana w klasach dziedziczonych po Prototype. Klient chcąc stworzyć nowy obiekt wywołuje metodę clone() pośrednio, za pomocą zdefiniowanej przez siebie operacji z parametrem określającym wymaganą docelową klasę realizującą abstrakcję Prototype.

Przykładowa implementacja

<?php
abstract class Prototype{
    protected $name;

    public function __construct($name) {
        $this->name=$name;
    }
     abstract function __clone();
    public function getName() {
        return $this->name;
    }
}

class ConcretePrototype extends Prototype{
    
    public function __construct($name) {
        parent::__construct($name);
    }
    public function __clone() {}
}

// testy
$prototype = new ConcretePrototype("nazwa");
echo  $prototype->getName(); // wyswietli "nazwa"
$prototype2 = clone $prototype;
echo  $prototype2->getName(); // wyswietli "nazwa"
?>

Przykład z życia wzięty

Przypuśćmy, że tworzymy księgarnię internetową. Istnieje potrzeba stworzenia wielu obiektów książek o podobnych właściwościach (wspólna kategoria, autor itp.). Zamiast ustawiać pola dla każdego obiektu oddzielnie możemy wykonać klony i zmieniać tylko elementy unikalne.

<?php
abstract class Book {
    protected $title;
    protected $topic;
    abstract function __clone();
    public function getTitle() {
        return $this->title;
    }
    public function setTitle($title) {
        $this->title = $title;
    }
    public function getTopic() {
        return $this->topic;
    }
}

class PHPBook extends Book {
    public function __construct() {
        $this->topic = 'PHP';
    }
    function __clone() {
    }
}

class JAVABook extends Book {
    public function __construct() {
        $this->topic = 'JAVA';
    }
    function __clone() {
    }
}
 
//testy
$phpbook1 = new PHPBook();
$phpbook1->setTitle("Ksiazka1");
$phpbook2 = clone $phpbook1;
$phpbook2->setTitle("Ksiazka2");

$javabook1 = new JAVABook();
$javabook1->setTitle("Ksiazka1");
$javabook2 = clone $javabook1;
$javabook2->setTitle("Ksiazka2");

echo "Kategoria: ".$phpbook1->getTopic()." Tytul: ".$phpbook1->getTitle()."<br />";
echo "Kategoria: ".$phpbook2->getTopic()." Tytul: ".$phpbook2->getTitle()."<br />";
echo "Kategoria: ".$javabook1->getTopic()." Tytul: ".$javabook1->getTitle()."<br />";
echo "Kategoria: ".$javabook2->getTopic()." Tytul: ".$javabook2->getTitle()."<br />";
?>

Zastosowanie

Wzorzec Prototype można stosować w sytuacjach, gdy tworzona jest duża liczba obiektów tego samego typu. Stosuje się go głównie w celach optymalizacji, gdyż klonowanie obiektu jest szybsze niż jego stworzenie.

PS: Może ktoś napisze miarodajne testy? ;)

Powiązane tematy

Autor wpisu: Marek, dodany: 03.07.2012 17:26, tagi: php, javascript

Literalna próba zapisu znaków specjalnych przy użyciu js powoduje dosłowne ich wyświetlenie:

<script type="text/javascript">
  alert('&raquo;')
  alert('&laquo;')
</script>

Możemy to zapisać również używając innej notacji:

<script type="text/javascript">
  alert('&#187;')
  alert('&#170;')
</script>

Ale i to zostaje przepisane. Aby uzyskać żądany efekt należy użyć funkcji fromCharCode oraz kodu danego znaku – dla &raquo; (czyli &#187;) będzie to 187:

<script type="text/javascript">
  alert(String.fromCharCode(187))
  alert(String.fromCharCode(171))
</script>

Autor wpisu: batman, dodany: 03.07.2012 10:00, tagi: zend_framework

Ostatnim razem podczas opisywania ZF2, skupiłem się tylko i wyłącznie na formularzach (siła przyzwyczajeń). Dzisiaj zaprezentuję sposób tworzenia modułów w Zend Framerowk 2. W przeciwieństwie do ZF1, moduły stanowią trzon aplikacji i w końcu można je bez większego problemu przenosić między aplikacjami.

Zanim jednak weźmiemy się za moduły, warto poznać nową strukturę katalogów ZF2, która znacząco odbiega od tego, do czego przyzwyczaił nas ZF1.

application_root/
    config/
        application.config.php
        autoload/
            global.php
            local.php
            // etc.
    data/
    module/
    vendor/
    public/
        .htaccess
        index.php

Najważniejszym katalogiem jest module, w którym znajdują się moduły aplikacji złożone z kontrolerów oraz widoków (o czym za chwilę). Równie ważnym katalogiem jest config, zawierający globalną konfigurację naszej aplikacji. Katalog vendor, jak sama nazwa wskazuje, zawiera wszystkie zewnętrzne biblioteki, z których będziemy korzystać, w tym ZF2.

Struktura pojedynczego modułu prezentuje się następująco.

module_root/
    Module.php
    autoload_classmap.php
    autoload_function.php
    autoload_register.php
    config/
        module.config.php
    public/
        images/
        css/
        js/
    src/
        <module_namespace>/
            <code files>
    view/
        <dir-named-after-module-namespace>/
            <dir-named-after-a-controller>/
                <.phtml files>

Plik Module.php odpowiada za inicjalizację modułu (jego odpowiednikiem w ZF1 jest Bootstrap.php). Pliki autoload_* służą do ładowania plików o niestandardowych nazwach (nie są wymagane). Katalog config zawiera konfigurację modułu, public – zasoby (grafiki, style oraz skrypty JavaScript), src – kontrolery, modele oraz wszystkie inne pliki wykorzystywane przez moduł, a view – widoki. Powyższa struktura katalogów została żywcem zaczerpnięta z dokumentacji do beta4 ZF2, a jej dokładny opis znajduje się pod adresem http://packages.zendframework.com/docs/latest/manual/en/zend.mvc.html#zend.mvc.intro.basic-application-structure.

Skoro wiemy już jak wygląda struktura katalogów w ZF2, pora utworzyć pierwszy moduł. Najlepiej będzie zabrać się do tego poprzez pobranie szkieletowej aplikacji dostępnej pod adresem https://github.com/zendframework/ZendSkeletonApplication/zipball/master. Po jej rozpakowaniu i uruchomieniu, zobaczymy coś takiego.

zf2_start_screen

Dodanie nowego modułu polega na utworzeniu w katalogu module podkatalogu o wybranej przez nas nazwie. Niech nazwą tą będzie Example. Wewnątrz katalogu Example musimy utworzyć katalogi config, src oraz view. Nie można zapomnieć o pliku Module.php. Zawartość klasy możemy bez żadnego problemu skopiować z pliku Module.php znajdującego się w module Application. Musimy tylko pamiętać o zmianie przestrzeni nazw na Example. Podejrzewam, że w finalnej wersji frameworka, plik ten będzie generowany automatycznie przez narzędzie wiersza poleceń.

Kolejnym krokiem będzie utworzenie kontrolera. W tym celu w katalogu src tworzymy katalog o takiej samej nazwie jak moduł (w naszym przypadku będzie to Example), a w nim katalog Controller. W tym katalogu tworzymy plik o nazwie IndexController.php.

namespace Example\Controller;

use Zend\Mvc\Controller\ActionController;
use Zend\View\Model\ViewModel;

class IndexController extends ActionController
{
    public function indexAction()
    {}
}

Kolejnym krokiem tworzenia modułu, jest dodanie widoku. Zrobimy to poprzez utworzenie wewnątrz katalogu view folderu o nazwie naszego modułu (example), a w nim struktury znanej z ZF1, czyli katalogu odpowiadającemu kontrolerowi (index) oraz pliku widoku odpowiadającemu akcji (index.phtml).

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

Autor wpisu: Łukasz Socha, dodany: 01.07.2012 16:20, tagi: php

pobierz w .pdf(przeznaczone do wydruku)

Druga część z serii wpisów o wzorcach projektowych. Dziś omówię wzorzec Strategii (Strategy).

 

Strategia jest wzorcem projektowym, który definiuje rodzinę wymiennych algorytmów i kapsułkuje je w postaci klas. Dzięki temu umożliwia wymienne stosowanie każdego z nich w trakcie działania programu.

Diagram klas wzorca Strategy

Przykładowa implementacja

<?php
interface AbstractStrategy{
    function task();
}
class ConcreteStrategy1 implements AbstractStrategy{
    public function task() {
        echo "Strategy 1";
    }
}
class ConcreteStrategy2 implements AbstractStrategy{
    public function task() {
        echo "Strategy 2";
    }
}
class Context{
    private $strategy;
    
    public function setStrategy(AbstractStrategy $obj) {
        $this->strategy=$obj;
    }
    public function getStrategy() {
        return $this->strategy;
    }
}
// testy
$obj = new Context();
$obj->setStrategy(new ConcreteStrategy1);
$obj->getStrategy()->task(); // wyswietla „Strategy 1”
$obj->setStrategy(new ConcreteStrategy2); 
$obj->getStrategy()->task(); // wyswietla „Strategy 2”
?>

We wzorcu tym definiujemy wspólny interfejs dla wszystkich strategii. Następnie, w poszczególnych klasach implementujemy metody z konkretnymi już algorytmami. Za pomocą klasy Context możemy łatwo zmieniać używaną strategię w trakcie działania aplikacji.

Przykład z życia wzięty

Przypuśćmy, że tworzymy sklep internetowy oferujący swoje usługi w kilku państwach. Jak wiadomo prawo podatkowe znacząco różni się w poszczególnych krajach. Powstaje problem naliczenia odpowiedniego podatku dla klientów pochodzących z odmiennych państw. Jak to rozwiązać? Wybrać odpowiednią stawkę za pomocą licznych instrukcji warunkowych? Nie, do tego świetnie nadaje się wzorzec strategii.

<?php

interface Tax{
    public function count($net);
}

class TaxPL implements Tax{
    public function count($net) {
        return 0.23*$net;
    }
}

class TaxEN implements Tax{
    public function count($net) {
        return 0.15*$net;
    }
}

class TaxDE implements Tax{
    public function count($net) {
        return 0.3*$net;
    }
}

class Context{
    private $strategy;
    
    public function setCountry($country) {
        switch ($country) {
            case "PL":
                $this->strategy = new TaxPL();
                break;
            case "DE":
                $this->strategy = new TaxDE();
                break;
            case "EN":
                $this->strategy = new TaxEN();
                break;
        }
    }
    public function getTax() {
        return $this->strategy;
    }
}

// testy
$tax = new Context();
$tax->setCountry("PL");
echo $tax->getTax()->count(100); // wyswietla "23"
$tax->setCountry("EN");
echo $tax->getTax()->count(100); // wyswietla "15"
$tax->setCountry("DE");
echo $tax->getTax()->count(100); // wyswietla "30"

?>

Za pomocą metody setCountry() możemy w bardzo prosty sposób wybierać odpowiednią strategię obliczania podatku dostosowaną do państwa, z którego pochodzi nasz klient. Jeżeli sklep zdecyduje się na poszerzenie działalności o kolejny kraj, wystarczy tylko dopisać odpowiednią klasę implementującą interfejs Tax oraz dodać do switch’a odpowiedni warunek.

Zalety i wady

Zalety:

  • Eliminacja instrukcji warunkowych – kod jest bardziej przejrzysty.
  • Umożliwia wybór implementacji – algorytmy mogą rozwiązywać ten sam problem, lecz różnić się uzyskiwanymi korzyściami.
  • Łatwość dołączania kolejnych strategii.
  • Łatwiejsze testowanie programu – można debugować każdą strategię z osobna.

Wady:

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

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