<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>webbricks &#187; PHP5</title>
	<atom:link href="http://blog.grzegorzpawlik.com/tag/php5/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.grzegorzpawlik.com</link>
	<description>Doświadczenie, to coś, co zdobywamy tuż po chwili w której było nam potrzebne ...</description>
	<lastBuildDate>Tue, 07 Feb 2012 10:09:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Immediately Invoked Function (IIF) w PHP</title>
		<link>http://blog.grzegorzpawlik.com/2011/03/immediately-invoked-function-iif-w-php/</link>
		<comments>http://blog.grzegorzpawlik.com/2011/03/immediately-invoked-function-iif-w-php/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 16:56:46 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[JS and friends]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1528</guid>
		<description><![CDATA[Bardzo fajnie jest móc w Javascript zrobić coś takiego: (function(x){ alert(x*2); })(); Pozwala Ci to izolować zmienne w funkcji i spać spokojnie. Chciałbym móc zrobić coś takiego w PHP, od wersji 5.3 są domknięcia, więc teoretycznie powinno się dać. Nie &#8230; <a href="http://blog.grzegorzpawlik.com/2011/03/immediately-invoked-function-iif-w-php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Bardzo fajnie jest móc w Javascript zrobić coś takiego:</p>
<pre name="code" class="javascript">
(function(x){
 alert(x*2);
})();
</pre>
<p>Pozwala Ci to izolować zmienne w funkcji i spać spokojnie. Chciałbym móc zrobić coś takiego w PHP, od wersji 5.3 są domknięcia, więc teoretycznie powinno się dać.<br />
Nie udało mi się jednak znaleźć tak zgrabnego rozwiązania. Jednak po kolei:</p>
<pre name="code" class="php">
$myFunc = function() {
  echo "123";
};
$myFunc();
</pre>
<p>Jednak ciągle mamy tu zmienną $myFunc &#8211; istnieje ryzyko, że właśnie ją komuś nadpisujemy.</p>
<pre name="code" class="php">
function inv($func){
  return $func();
}
inv(function(){ alert "123"; });
</pre>
<p>Już lepiej. Jednak chciałbym móc przekazać parametr(y) do mojej IIF. Dlatego finalna wersja mogłaby wyglądać tak:</p>
<pre name="code" class="php">
function inv($func){
  $args = array();
  if(func_num_args() >1) {
    $args = func_get_args();
    unset($args[0]);
  }
  return call_user_func_array($func, $args);
}

$x = "global x";
inv(function($x){
  echo "=== inside";
  var_dump($x);
  $x = "local x";
  var_dump($x);
  echo "=== /";
}, $x);
var_dump($x);
</pre>
<p>Dzięki temu masz miło wyizolowaną zmienną $x i wszyscy są szczęśliwi. Taki mały <a href="http://thedailywtf.com/Series/CodeSOD.aspx">Snippet of the day</a>. </p>
<p>Tak z zupełnie innej beczki: znalazłem dziś w kodzie zmienną $Darek ;)</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2011/03/immediately-invoked-function-iif-w-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lazy Loading w modelach a&#8217;la CakePHP</title>
		<link>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/#comments</comments>
		<pubDate>Tue, 19 Oct 2010 14:29:07 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Inne]]></category>
		<category><![CDATA[lazy loading]]></category>
		<category><![CDATA[model]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1383</guid>
		<description><![CDATA[CakePHP nie wspiera aktualnie leniwego ładowania obiektów. W prawdzie są jakieś skrypty, ale jeszcze żaden z nich nie chciał mi zadziałać. Zacząłem się zastanawiać: &#8220;Gdybym miał zaimplementować Lazy Loading w warstwie modelu, to jak bym to zrobił?&#8221; To co mi &#8230; <a href="http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>CakePHP nie wspiera aktualnie leniwego ładowania obiektów. W prawdzie są jakieś skrypty, ale jeszcze żaden z nich nie chciał mi zadziałać. Zacząłem się zastanawiać: &#8220;Gdybym miał zaimplementować Lazy Loading w warstwie modelu, to jak bym to zrobił?&#8221; To co mi wyszło korzysta z rozwiązań PHP5, więc może wsparcie gałęzi 1.x Cake&#8217;a dla PHP4 implikuje brak tego wzorca?</p>
<p>Tak czy siak, podzielę się z Wami moimi pomysłami.</p>
<p>Zacznijmy od początków. Przeciwieństwem leniwego ładowania jest ładowanie gorliwe (lub zachłanne), to znaczy ładowanie na zapas wszystkiego co może okazać się potrzebne. Warstwa modelu w cake&#8217;u mogła by wyglądać tak (duże uproszeczenie, jedyna możliwa relacja to belongsTo).</p>
<h4>Eager Loading</h4>
<pre name="code" class="php">

class Model {
	function find(){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."`";

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
		if(isset($this->belongsTo) &#038;&#038; is_array($this->belongsTo)){
			foreach($this->belongsTo as $related){
					$this->{$related} = new $related();
			}
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group");
}

class Group extends Model{
}

$user =  new User();

$user->find();
echo "<br/>";
$user->Group->find();
</pre>
<p>Widzimy, że obiekt Group jest inicjowany długo przed użyciem:</p>
<pre>
creating User
creating Group
calling User::find()

string 'Select * from `users` as `User`' (length=31)

calling Group::find()

string 'Select * from `groups` as `Group`' (length=33)
</pre>
<p>Mało tego, ładuje się nawet jeśli nigdy nie wywołamy metody w tym obiekcie (zakomentuj $user->Group->find(); żeby się o tym przekonać).</p>
<h4>Lazy Loading</h4>
<pre name="code" class="php">
class Model {
	function find(){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."`";

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
	}

	function __get($property){
		if(!isset($this->{$property})) {
			$this->{$property} = new $property();
		}
		if(in_array($property, $this->belongsTo)){
			return $this->{$property};
		}else{
			$r = new ReflectionClass($this);
			throw new Exception(
				"$property not related with {$r->getName()}"
			);
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group");
}

class Group extends Model{
}

$user =  new User();

$user->find();
echo "<br/>";
$user->Group->find();
</pre>
<p>W tym rozwiązaniu obiekt Group nie zostanie stworzony, dopóki nie będziemy go potrzebować. Wykorzystana jest do tego magiczna metoda __get, która jest wywoływana wtedy, gdy próbujemy się odnieść do atrybutu obiektu, który nie istnieje.</p>
<p>ale pozostaje kwestia wyszukiwania w modelu tak, żeby zwrócił powiązane dane<br />
np.: var_dump($user->find());</p>
<pre>
array
  0 =>
    array
      'User' =>
        array
          'id' => int 1
          'name' => string 'Franek' (length=6)
      'Group' =>
        array
          'id' => int 1
          'name' => string 'Admin' (length=5)
  1 =>
    array
      'User' =>
        array
          'id' => int 2
          'name' => string 'Janek' (length=5)
      'Group' =>
        array
          'id' => int 10
          'name' => string 'User' (length=4)
</pre>
<p>Wiemy, że belongsTo oznacza, że User powinien mieć pole (group_id), zatem query powinno wyglądać mniej więcej tak:</p>
<pre name="code" class="sql">
Select User.id, User.name, Group.id, Group.name from users as User
Inner Join groups as Group on (User.group_id = Group.id)
</pre>
<p>W tej chwili cakephp zawiera behavior containable, jednak on polega na załadowanych już modelach.</p>
<p>Zostańmy przy terminologii cake&#8217;a i przyjmijmy, że będziemy przekazywać informacje na temat powiązanych elementów w zmiennej $contain</p>
<p>function find($contain=null)</p>
<p>Teraz chciałbym powiedzieć: obiekcie $user zwróć mi wszystkich użytkowników z bazy wraz z informacją w jakich są grupach:</p>
<pre name="code" class="php">
$user =  new User();

$user->find(
	array(
		"belongsTo" => array($user->Group)
	)
);
</pre>
<p>Zatem zaktualizuję metodę find:</p>
<pre name="code" class="php">
	function find($contain=null){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."` \n";

		if(!empty($contain) &#038;&#038; is_array($contain["belongsTo"])){
			foreach($contain["belongsTo"] as $belongsToModel){
				$query .= $belongsToModel->getBelongsToJoin($this);
			}
		}

		var_dump($query);

		//return query result
	}
</pre>
<p>która od teraz zakłada, że model posiada metodę, która potrafi utworzyć odpowiedniego joina:</p>
<pre name="code" class="php">
	function getBelongsToJoin($obj){
		$owner_reflection = new ReflectionObject($this);
		$obj_reflection = new ReflectionObject($obj);
		return "INNER JOIN `".strtolower($owner_reflection->getName())."s`
					as `".$owner_reflection->getName()."`
						ON (`".$owner_reflection->getName()."`.`id` = `".$obj_reflection->getName()."`.`".strtolower($owner_reflection->getName())."_id`)
					";
	}
</pre>
<p>to jest oczywiście bardzo brzydkie ze względu na refleksje. Metodę getName() można jednak zaimplementować w klasie Model.</p>
<p>Tak czy siak, w tym wypadku nawet w find()&#8217;zie uwzględniającym relacje można stosować Lazy Loading</p>
<p>Na koniec całość kodu, z większą ilością relacji i przykładem działania (i małym refactoringiem &#8211; skoro już mowa o lazy loading to głupio było ładować piętnaście razy ReflectionClass):</p>
<pre name="code" class="php">

class ModelException extends Exception{}

class Model {

	protected $reflection = null;

	function getName(){
		if(is_null($this->reflection)){
			$this->reflection= new ReflectionClass($this);
		}
		return $this->reflection->getName();
	}

	function getTableName(){
		return strtolower( $this->getName() . "s" );
	}

	function getFKName(){
		return strtolower( $this->getName() . "_id");
	}

	function getBelongsToJoin($obj){
		$owner_reflection = new ReflectionObject($this);
		$obj_reflection = new ReflectionObject($obj);
		return "INNER JOIN `".$this->getTableName()."`
					as `".$this->getName()."`
						ON (`".$this->getName()."`.`id` = `".$obj->getName()."`.`".$this->getFKName()."`)
					";
	}

	function find($contain=null){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "\nSelect * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."` \n";

		if(!empty($contain) &#038;&#038; is_array($contain["belongsTo"])){
			foreach($contain["belongsTo"] as $belongsToModel){
				$query .= $belongsToModel->getBelongsToJoin($this);
			}
		}

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
	}

	function __get($property){
		if(!isset($this->{$property})) {
			$this->{$property} = new $property();
		}
		if(in_array($property, $this->belongsTo)){
			return $this->{$property};
		}else{
			$r = new ReflectionClass($this);
			throw new ModelException(
				"$property not related with {$r->getName()}"
			);
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group", "Unit", "Company");
}

class Group extends Model{
}

class Unit extends Model{}
class Company extends Model{}

$user =  new User();

$user->find(
	array(
		"belongsTo" => array($user->Unit, $user->Company)
	)
);
</pre>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>__autoload()</title>
		<link>http://blog.grzegorzpawlik.com/2010/10/__autoload/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/10/__autoload/#comments</comments>
		<pubDate>Wed, 13 Oct 2010 14:53:06 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1369</guid>
		<description><![CDATA[Uwaga: Tylko PHP>5! Gdy definiujesz w PHP każdą klasę osobno może Cię zainteresować magiczna funkcja __autoload() Załóżmy, że masz pliki MyClass1.php i MyClass2.php z klasami MyClass1 i MyClass2, wtedy zamiast: include_once("MyClass1.php"); include_once("MyClass2.php"); $obj1 = new MyClass1; $obj2 = new MyClass2; &#8230; <a href="http://blog.grzegorzpawlik.com/2010/10/__autoload/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>Uwaga: Tylko PHP>5!</strong></p>
<p>Gdy definiujesz w PHP każdą klasę osobno może Cię zainteresować magiczna funkcja <a href="http://pl.php.net/__autoload">__autoload()</a></p>
<p>Załóżmy, że masz pliki MyClass1.php i MyClass2.php z klasami MyClass1 i MyClass2, wtedy zamiast:</p>
<pre name="code" class="php">
include_once("MyClass1.php");
include_once("MyClass2.php");

$obj1 = new MyClass1;
$obj2 = new MyClass2;
</pre>
<p>Możesz zdefiniować funckję __autoload():</p>
<pre name="code" class="php">
function __autoload($class_name){
	include_once($class_name . ".php");
}

$obj1 = new MyClass1;
$obj2 = new MyClass2;
</pre>
<p>W wielkich projektach można zaoszczędzić trochę klepania. </p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/10/__autoload/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Preload obrazków w cakePHP</title>
		<link>http://blog.grzegorzpawlik.com/2008/03/preload-obrazkow-w-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2008/03/preload-obrazkow-w-cakephp/#comments</comments>
		<pubDate>Wed, 05 Mar 2008 09:00:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[DRY]]></category>
		<category><![CDATA[PHP4]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://meta.vipserv.org/blog.grzegorzpawlik.com/?p=17</guid>
		<description><![CDATA[Dla tych, dla których DRY nie jest tylko angielskim słowem napisanym z niewiadomych przyczyn wielkimi literami. Usprawnienie preloadu obrazków w cakePHP może przydać się, gdy chcesz wyświetlić typową galerię: miniaturki + duży obraz zmieniający się po kliknięciu w miniaturkę. Kiedy &#8230; <a href="http://blog.grzegorzpawlik.com/2008/03/preload-obrazkow-w-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Dla tych, dla których DRY nie jest tylko angielskim słowem napisanym z niewiadomych przyczyn wielkimi literami.<br />
Usprawnienie preloadu obrazków w cakePHP może przydać się, gdy chcesz wyświetlić typową galerię: miniaturki + duży obraz zmieniający się po kliknięciu w miniaturkę. Kiedy zaimplementujesz preload obrazków, to znaczy, że jesteś świadomym budowniczym stron internetowych. Wiesz, że możesz wykorzystać przeglądarkę do przyśpieszenia działania Twojej strony, a nie tylko jej biernego wyświetlania. Zatem do dzieła!</p>
<p>Moje podejście obiera się na tzw. elemencie. Element to (wg. manuala CakePHP) &#8220;częściowy layout&#8221;. Czyli cegiełki, z których możesz stworzyć swoje strony.</p>
<p>app/views/elements/image_preload.thtml:</p>
<p><code lang="html"><br />
<?php $images_count = count($images); $i = 0;?><br />
<script type="text/javascript"> //<![CDATA[  var _images = new Array(); 
  <?php $i=0; foreach($images as $image): ?>    
    _images[<?= $i ?>] = new Image();   _images[<?= $i++ ?>].src = '<?= 'http://'.$_SERVER['SERVER_NAME'].$this->webroot.'img/'.$subdir.$image['filename'] ?>';     
   <?php endforeach; ?>       //]]&gt; </script><br />
</code></p>
<p>O co chodzi? Wpisz w google &#8220;javascript image preload&#8221; to się dowiesz :)<br />
Jak z tego skorzystać? Już śpieszę z wyjaśnieniami:<br />
W pliku Twojego layoutu, na samym końcu (przed &lt;/body&gt; ofcoz) dodaj:<br />
<code lang="php"><br />
 <?php if(isset($images_to_preload) &#038;&#038; isset($images_to_preload_subdir)): ?><br />
    <?= $this->renderElement('image_preload', array('images'=>$images_to_preload, 'subdir'=>$images_to_preload_subdir)); ?><br />
 <?php endif; ?><br />
</code></p>
<p>A w metodzie odpowiedniego kontrolera wygeneruj tablicę z nazwami plików, które chcesz załadować do cache przeglądarki i przekaż do layoutu:<br />
<code lang="php"><br />
$this->set('images_to_preload', $content['Photo']);<br />
$this->set('images_to_preload_subdir', 'photos/');<br />
</code></p>
<p>U mnie akurat model Photo ma pole filename, więc tak napisałem sobie element. Do tego dodałem możliwość przekazania z kontrolera podkatalogu w którym są obrazki w /app/webroot/img (teraz już nie wiem dlaczego, ale jak to pisałem, na pewno było to uzasadnione ;))</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2008/03/preload-obrazkow-w-cakephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP + nuSOAP serwer + autoryzacja othAuth</title>
		<link>http://blog.grzegorzpawlik.com/2008/02/cakephp-nusoap-serwer-autoryzacja-othauth/</link>
		<comments>http://blog.grzegorzpawlik.com/2008/02/cakephp-nusoap-serwer-autoryzacja-othauth/#comments</comments>
		<pubDate>Thu, 21 Feb 2008 14:23:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[autoryzacja]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[othAuth]]></category>
		<category><![CDATA[PHP4]]></category>
		<category><![CDATA[PHP5]]></category>
		<category><![CDATA[SOAP]]></category>

		<guid isPermaLink="false">http://meta.vipserv.org/blog.grzegorzpawlik.com/?p=15</guid>
		<description><![CDATA[Autoryzacja serwera soap jest o tyle niewdzięczna, o ile nie została zaimplementowana w samym protokole. Do tego sam cake nie ułatwia nam sprawy. Jednak po wielu dniach zmagań udało mi się nagiąć tą materię i z chęcią się doświadczeniami podzielę &#8230; <a href="http://blog.grzegorzpawlik.com/2008/02/cakephp-nusoap-serwer-autoryzacja-othauth/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Autoryzacja serwera soap jest o tyle niewdzięczna, o ile nie została zaimplementowana w samym protokole. Do tego sam cake nie ułatwia nam sprawy. Jednak po wielu dniach zmagań udało mi się nagiąć tą materię i z chęcią się doświadczeniami podzielę (tak naprawdę po uporaniu się z tym problemem postanowiłem założyć ten blog).<br />
Załóżmy, że serwer, który opisałem w poprzednim poście już stoi. Załóżmy też, że jest zrobione wszystko, co musiało być zrobione, aby autoryzacja othAuth działała w naszej aplikacji (jeśli nie- przed kontynuowaniem zapraszam <a href="http://bakery.cakephp.org/articles/view/othauth-0-5-documentation">tutaj</a> )<br />
Po pierwsze zmieńmy w /app/config/core.php definicję stałej CAKE_SESSION_COOKIE na &#8216;PHPSESSID&#8217; bez tego nie uda nam się &#8220;odzyskać&#8221; sesji znając jej id.<br />
Dalej&#8230; logowanie &#8220;normalnego&#8221; usera wygląda mniej więcej tak:<br />
/app/controllers/user_controller.php<br />
<code lang="php"><br />
function login()<br />
{<br />
if(isset($this->params['data']))<br />
{<br />
$auth_num = $this->othAuth->login($this->params['data']['User']);<br />
return $auth_num;// potrzebne dla soap-owego logowania (1 jeśli udane)<br />
}<br />
}<br />
</code></p>
<p>Wracajmy teraz szybko do naszego oczka w głowie: /app/controllers/soap_controller.php.<br />
W metodzie _defineTypes dodajmy definicję złożonych typów:<br />
<code lang="php"><br />
$this->server->wsdl->addComplexType (<br />
'LoginData',<br />
'complexType',<br />
'struct',<br />
'all',<br />
'',<br />
array(<br />
'Login' => array('name' => 'Login',<br />
'type' => 'xsd:string'), //<br />
'Password' => array('name' => 'Password',<br />
'type' => 'xsd:string'), //<br />
)<br />
);<br />
/**<br />
* odpowiedĹş na logowanie<br />
*/<br />
$this->server->wsdl->addComplexType (<br />
'LoginResponse',<br />
'complexType',<br />
'struct',<br />
'all',<br />
'',<br />
array(<br />
'Result' => array('name' => 'Result',<br />
'type' => 'xsd:int'), //<br />
'SID' => array('name' => 'SID',<br />
'type' => 'xsd:string'), //<br />
)<br />
);<br />
</code></p>
<p>A w metodzie _registerMethods() zarejestrujmy metodę SoapController.login:<br />
<code lang="php"><br />
$this->server->register('SoapController.login',<br />
array('SoapParam' => 'tns:LoginData'),<br />
array('return' => 'tns:LoginResponse') ,<br />
$this->namespace,<br />
$this->namespace . '#login',<br />
'logowanie'<br />
);<br />
</code></p>
<p>Teraz właściwe logowanie:<br />
<code lang="php"><br />
function login($login_data) {<br />
$login_details['User'] = array('username' => $login_data['Login'],<br />
'password' => $login_data['Password'],<br />
'cookie' => "0");<br />
$result = $this->requestAction('users/login', array('data' => $login_details));<br />
return array('Result' => $result, 'SID' => session_id() );<br />
}<br />
</code></p>
<p>Kilka słów wyjaśnienia: w pierwszej linii tworzymy trochę sztucznie tablicę $login_details, która jest taka jaką oczekuje metoda UsersController::login() (zgodnie z zasadą <a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> nie będziemy tworzyć osobnej metody dla logowania przez soap). Wywołujemy akcję users/login przekazując do niej spreparowane dane i zwracamy zgodnie z definicją typu LoginResponse- tablicę z danymi.</p>
<p>To była ta łatwiejsza część. Teraz to nad czym spędziłem kilka intensywnych dni- jak odzyskać sesję??<br />
Od razu sprostowanie: wygląda na to, że sesja &#8220;trzyma się&#8221; tak długo, jak długo klient się nie rozłączy (testowałem to za pomocą również nuSOAP + php i w okresie trwania skryptu &#8211; było ok). Jednak ja potrzebowałem możliwości podania przez klienta id sesji, którą otrzymał podczas logowania. Jak to zrobić?<br />
Po kolei:<br />
Dodajmy typ, który będzie służył do przekazania informacji na temat zalogowanego usera:<br />
<code lang="php"><br />
$this->server->wsdl->addComplexType (<br />
'UserDetails',<br />
'complexType',<br />
'struct',<br />
'all',<br />
'',<br />
array(<br />
'Id' => array('name' => 'id',<br />
'type' => 'xsd:int'), //<br />
'Username' => array('name' => 'Username',<br />
'type' => 'xsd:string'), //<br />
'Email' => array('name' => 'Email',<br />
'type' => 'xsd:string'), //<br />
'GroupId' => array('name' => 'GroupId',<br />
'type' => 'xsd:string'), //<br />
'Error' => array('name' => 'Error',<br />
'type' => 'xsd:string')<br />
)<br />
);<br />
</code></p>
<p>Zarejestrujmy metodę SoapController.check():<br />
<code lang="php"><br />
$this->server->register('SoapController.check',<br />
array('data' => 'xsd:string'),<br />
array('return' => 'tns:UserDetails') ,<br />
$this->namespace,<br />
$this->namespace . '#check',<br />
'logowanie'<br />
);<br />
</code></p>
<p>I zdefiniujmy ją:<br />
<code lang="php"><br />
function check($data="") {<br />
$data = $this->requestAction('/users/soapCheck', array('data' => $data, 'SID'=>$data));<br />
$this->log($data, LOG_DEBUG);</p>
<p>if($data){<br />
$return = array ( 'Id' => $data['User']['id'],<br />
'Username' => $data['User']['username'],<br />
'Email' => $data['User']['email'],<br />
'GroupId' => $data['Group']['id'],<br />
'Error' => 'ewrifing ok');<br />
}else{<br />
$return = array ( 'Id' => -1,<br />
'Username' => ''<br />
'Email' => '',<br />
'GroupId' => '',<br />
'Error' => 'not logged in');<br />
}</p>
<p>return $return ;<br />
}<br />
</code></p>
<p>Wyjaśnienia: oprócz danych jak przy logowaniu pojawiło się jeszcze pole w tablicy &#8216;SID&#8217;, to tędy przekażemy informację, że chcemy &#8220;wymusić&#8221; jakieś id sesji.<br />
Teraz metoda UsersController::soapCheck():<br />
<code lang="php"><br />
function soapCheck($data) {<br />
return $this->othAuth->getData();<br />
}<br />
</code></p>
<p>No i na koniec gdzieś musimy wymusić inne id sesji (no bo nie ma przeglądarki, ani kochanych cookies, które zrobią to za nas). W app/app_controller.php:<br />
<code lang="php"><br />
if(isset($this->params['SID'])){<br />
session_id($this->params['SID']);<br />
}<br />
</code></p>
<p>I ok&#8230; jaaasne. Nie działa, prawda? Dlaczego? A to dlatego, że w momencie kiedy klient SOAP łączy się z naszym ukochanym serwerem SOAP wygląda to tak:<br />
<code>request (soap/index) -> app_controller -> soap_contropper -> check -> request (/users/soapCheck) -> app_controller*->users_controller -> soapCheck ...</code></p>
<p>miejsce oznaczone gwiazdką oznacza moment, kiedy app_controller dostaje info o tym, że jest jakieś SID&#8230; tylko że wtedy to już jest za późno (jak to mówią ślązocy: &#8220;po ptokach&#8221; :P) $this-&gt;requestAction to moment, kiedy ciasteczka już nie są wysyłane i szanowna pani Sesja ma już głęboko w &#8230; nosie fakt, że tam jakieś id jest przesyłane.<br />
Nie lękaj się jednak, jest na to rada:<br />
zmień w app_controller poprzednio dodany blok na:<br />
<code lang="php"><br />
if(isset($this->params['url']['SID'])){<br />
session_id($this->params['url']['SID']);<br />
}<br />
</code></p>
<p>I teraz łącząc się z serwerem do linku /twoja_aplikacja/soap/ doklejaj parametr SID=. W takim wypadku już przy pierwszym request-&gt;app_controller id sesji trafia do &#8220;świadomości&#8221; Sesji i zazwyczaj rozpatruje je pozytywnie.</p>
<p>Nie jest to może najbardziej eleganckie rozwiązanie. Jednak najlepsze na jakie teraz mnie stać ;) Jeśli masz coś fajnieszego- pisz w komentarzach, z chęcią się podszkolę :)</p>
<p><span id="formatbar_Buttons" style="display: block;"></span></p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2008/02/cakephp-nusoap-serwer-autoryzacja-othauth/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP + serwer SOAP (pierwsze kroki)</title>
		<link>http://blog.grzegorzpawlik.com/2008/02/cakephp-serwer-soap-pierwsze-kroki/</link>
		<comments>http://blog.grzegorzpawlik.com/2008/02/cakephp-serwer-soap-pierwsze-kroki/#comments</comments>
		<pubDate>Tue, 19 Feb 2008 10:09:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP4]]></category>
		<category><![CDATA[PHP5]]></category>
		<category><![CDATA[SOAP]]></category>
		<category><![CDATA[WebServices]]></category>

		<guid isPermaLink="false">http://meta.vipserv.org/blog.grzegorzpawlik.com/?p=14</guid>
		<description><![CDATA[Mimo tego, że cake chwali się, że jest WebServices ready tyczy się to jedynie tak zwanego routingu (np. gdy użyjesz users/index &#8211; będzie &#8220;normalnie&#8221; czyli wyrenderuje widok /vews/users/index.thtml, a gdy &#8220;xml/user/index&#8221; &#8211; /views/users/xml/index.thtml) ale nie jest to prawdziwe WebSerwice. Nie &#8230; <a href="http://blog.grzegorzpawlik.com/2008/02/cakephp-serwer-soap-pierwsze-kroki/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Mimo tego, że cake chwali się, że jest WebServices ready tyczy się to jedynie tak zwanego routingu (np. gdy użyjesz users/index &#8211; będzie &#8220;normalnie&#8221; czyli wyrenderuje widok /vews/users/index.thtml, a gdy &#8220;xml/user/index&#8221; &#8211; /views/users/xml/index.thtml) ale nie jest to prawdziwe WebSerwice. Nie wiem ile jest sposobów na zmianę CakePHP w prawdziwy serwer SOAP, ja znam jeden i Wam go pokażę.</p>
<p>1. Potrzebna nam biblioteka SOAP (jako prawdziwy programiści pozwalamy, żeby najtrudniejsze rzeczy pisali za nas inni). Ja wybrałem NuSOAP. Wrzuć pliki do /app/vendors/nusoap/*.<br />
2. Stwórz kontroler do obsługi Soap. W moim przypadku soap_controller.php</p>
<p><code lang="php"><br />
/**<br />
* Soap Serwer<br />
* @package package<br />
*/<br />
vendor('nusoap/nusoap');<br />
class SoapController extends AppController {<br />
var $name = 'Soap';<br />
var $server;<br />
var $namespace;<br />
var $layout = 'blank';<br />
var $components = array('othAuth','Conf');<br />
var $othAuthRestrictions = null;</p>
<p>/**<br />
* Inicjalizacja ustawień serwera<br />
*<br />
*/<br />
function _init() {<br />
$this->namespace = $this->Session->host;<br />
$this->server = new soap_server();<br />
$this->server->debug_flag = false;<br />
$this->server->configureWSDL('MyWsdl', $this->namespace, 'http://'.$_SERVER['HTTP_HOST'] . $this->webroot . $this->params['controller'] ); // należy nadpisać endpoint, gdyż domyślnie ustawi się na /twoja_aplikacja/app/index.php<br />
$this->server->wsdl->schemaTargetNamespace = $this->namespace;<br />
$this->_defineTypes();<br />
$this->_registerMethods();<br />
}</p>
<p>/**<br />
* Serwer endpoint handler<br />
*<br />
*/<br />
function index(){<br />
Configure::write('debug', 0);</p>
<p>$this->_init();</p>
<p>$HTTP_RAW_POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';<br />
$this->server->service($HTTP_RAW_POST_DATA);<br />
exit();<br />
}</p>
<p>/**<br />
* define types required by this server<br />
* przyda się później<br />
*/<br />
function _defineTypes() {</p>
<p>}</p>
<p>function _registerMethods() {</p>
<p>$this->server->register(<br />
'SoapController.hello', // method name<br />
array('name' => 'xsd:string'), // input parameters<br />
array('return' => 'xsd:string'),<br />
$this->namespace,<br />
$this->namespace . '#hello',<br />
'document', // style<br />
'encoded' // use<br />
);</p>
<p>}</p>
<p>function hello($name){<br />
return array('return' => 'hello, '.$name);<br />
}<br />
}<br />
</code></p>
<p>3. Do tego stwórz model, który nic nie robi :)<br />
<code lang="php"><br />
class Soap extends AppModel {<br />
var $name = 'Soap';<br />
var $useTable = false;<br />
}<br />
</code></p>
<p>Teraz objaśnienia:<br />
<span style="font-style: italic;">SoapController::index()</span> &#8211; to twój endpoint serwera SOAP. <span style="font-style: italic;">SoapController::_registerMethods() </span>zajmuje się rejestrowaniem metod dostępnych przez serwer. W tym wypadku tylko jednej: SoapController::hello (notacja metod soap to Klasa.metoda).<br />
Metoda hello przyjmuje string jako parametr i zrwaca &#8216;Hello, &#8216;+ to co zostało jej przekazane.<br />
Przykładowy klient (poza cake.php):</p>
<p><code lang="php"></p>
<p>require('./nusoap/nusoap.php');</p>
<p>function pr($var) {<br />
echo "
<pre>";
var_dump($var);
echo "</pre>
<p>";<br />
}</p>
<p>/* create client */<br />
$endpoint = "http://localhost/meta/application_in_cakePHP/soap/index";</p>
<p>$mynamespace = "";<br />
$client = new soapclient($endpoint);</p>
<p>$err = $client->getError();<br />
if ($err) {<br />
// Display the error<br />
echo 'Constructor error: '. $err . ' ';<br />
// At this point, you know the call that follows will fail<br />
}</p>
<p>$response = $client->call('SoapController.hello', array('name' =>"Greg"));<br />
echo "Call SoapController.hello";<br />
if ($client->fault) {<br />
echo 'Fault: ';<br />
print_r($response);<br />
echo ' ';<br />
} else {<br />
// Check for errors<br />
$err = $client->getError();<br />
if ($err) {<br />
// Display the error<br />
echo 'Error: '. $err . '';<br />
} else {<br />
echo('response:');<br />
pr($response);<br />
}<br />
}</p>
<p></code></p>
<p>I ładny response:</p>
<pre>string(11) "hello, Greg"</pre>
<p>Dodatkowo, gdy w przeglądarce wpiszesz ścieżkę do kontrolera Soap zobaczysz ładną dokumentację Twojego serwera. A dodając do ścieżki ?wsdl &#8211; dokument wsdl.</p>
<p>żródła:<br />
<a href="http://www.scottnichol.com/nusoapprog.htm">http://www.scottnichol.com/nusoapprog.htm</a><br />
<a href="http://dietrich.ganx4.com/nusoap/faq.php">http://dietrich.ganx4.com/nusoap/faq.php</a></p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2008/02/cakephp-serwer-soap-pierwsze-kroki/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AppController::beforeFilter() &#8211; nagle nie działa?</title>
		<link>http://blog.grzegorzpawlik.com/2008/02/appcontrollerbeforefilter-nagle-nie-dziala/</link>
		<comments>http://blog.grzegorzpawlik.com/2008/02/appcontrollerbeforefilter-nagle-nie-dziala/#comments</comments>
		<pubDate>Mon, 18 Feb 2008 09:49:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[AppController]]></category>
		<category><![CDATA[beforeFilter]]></category>
		<category><![CDATA[PHP4]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://meta.vipserv.org/blog.grzegorzpawlik.com/?p=13</guid>
		<description><![CDATA[Jeśli od jakiegoś czasu używasz CakePHP, to wiesz, że istnieje metoda kontrolera beforeFilter(). Jest to tak zwany callback wywoływany tuż po wykonaniu akcji kontrolera, ale przed renderowaniem widoku (źródło). Dość częstą praktyką jest definiowanie metody w pliku app/app_controller.php, w klasie &#8230; <a href="http://blog.grzegorzpawlik.com/2008/02/appcontrollerbeforefilter-nagle-nie-dziala/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Jeśli od jakiegoś czasu używasz CakePHP, to wiesz, że istnieje metoda kontrolera <span style="font-style: italic;">beforeFilter(). </span>Jest to tak zwany callback wywoływany tuż po wykonaniu akcji kontrolera, ale przed renderowaniem widoku (<a href="http://api.cakephp.org/class_controller.html#e756a803e378b692480ba0ab107c9bee">źródło</a>).</p>
<p>Dość częstą praktyką jest definiowanie metody w pliku app/app_controller.php, w klasie AppController po to, aby wykonywać w niej operacje, które zawsze muszą być wykonane przed każdą akcją kontrolera. Jednak czasem zdarza się, że jednocześnie inny programista zdefiniuje metodę <span style="font-style: italic;">beforeFilter()</span> w swoim kontrolerze, wtedy niedoświadczony programista może mieć problemy ze znalezieniem przyczyny &#8220;dlaczego mój beforeFilter() w AppController się nie odpala?&#8221;.<br />
Powód jest prosty, jeśli dobrze rozumiemy zasady OOP (których w tym blogu przybliżał raczej nie będę). Zatem jeśli chcemy, aby nasza <span style="font-style: italic;">AppController::beforeFilter()</span>  odpalała się zawsze, niezależnie od tego, czy w jakim kontrolerze jest zdefiniowana <span style="font-style: italic;">beforeFilter()</span> czy też nie, należy w kontrolerach umieszczać wywołanie metody z klasy AppController. Osiągamy to oczywiście za pomocą <span style="font-style: italic;">parent::beforeFilter()</span>.<span style="font-style: italic;"></span></p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2008/02/appcontrollerbeforefilter-nagle-nie-dziala/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

