<?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>Athlan • Piotr Pelczar • blog programisty &#187; PHP</title>
	<atom:link href="http://athlan.pl/kategoria/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://athlan.pl</link>
	<description>Napisać kod zrozumiały dla komputera potrafi byle głupek. Dobrzy programiści tworzą kod zrozumiały dla człowieka...</description>
	<lastBuildDate>Tue, 14 Feb 2012 14:33:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>PHP cache, semafory</title>
		<link>http://athlan.pl/php-cache-semafory/</link>
		<comments>http://athlan.pl/php-cache-semafory/#comments</comments>
		<pubDate>Sat, 11 Jun 2011 22:11:46 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Optymalizacja]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Solutions]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=728</guid>
		<description><![CDATA[Praktyka cache&#8216;owania danych jest powszechna wśród programistów aplikacji webowych ze względu na optymalizację dostępu do danych bezpośrednio ze źródła ich pochodzenia, a w szczególności: trudność dostępu (np. wykonanie skomplikowanych połączeń), ograniczenia dostępu (np. limit odpytywania), długi czas oczekiwania na dane; powodów jest wiele. O ile tematyką stworzenia samego mechanizmu cache zajęli się m.in. Nospor, możecie [...]]]></description>
			<content:encoded><![CDATA[<p>Praktyka <strong>cache</strong>&#8216;owania danych jest powszechna wśród programistów aplikacji webowych ze względu na optymalizację dostępu do danych bezpośrednio ze źródła ich pochodzenia, a w szczególności:</p>
<ul>
<li>trudność dostępu (np. wykonanie skomplikowanych połączeń),</li>
<li>ograniczenia dostępu (np. limit odpytywania),</li>
<li>długi czas oczekiwania na dane; powodów jest wiele.</li>
</ul>
<p>O ile tematyką stworzenia samego mechanizmu cache zajęli się m.in. <a href="http://forum.php.pl/klasa-Cache-t49472.html" rel="nofollow">Nospor</a>, możecie podejrzeć jak to wygląda w <a href="http://framework.zend.com/manual/en/zend.cache.html" rel="nofollow">Zend_Cache</a>, Symfony, czy <a href="http://docs.kohanaphp.com/libraries/cache" rel="nofollow">Kohana</a>; tak ja chciałbym zwrócić uwagę na jeszcze jedną rzecz.</p>
<p>Zazwyczaj schemat kodu wygląda mniej więcej tak:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #339933;">&lt;</span> ?php
<span style="color: #000088;">$oCache</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Cache<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// tworzony jest jakis obiekt cache</span>
&nbsp;
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">expired</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">3600</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span> <span style="color: #339933;">!</span><span style="color: #990000;">is_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aData</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">// sprawdzamy, czy jest cache i nie wygasł</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #000088;">$aData</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oModel</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">GetSomething</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// zbieramy dane z bazy danych</span>
  <span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">save</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aData</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// $aData przechowuje nasze dane do użytku</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<h2>Symulacja, parę linijek kodu, a ile nieszczęść.</h2>
<p>Wszystko działa pięknie, dopóki nie spotkamy się z sytuacją, gdy setki osób (procesów) jednocześnie zechcą zbierać takie dane z bazy danych. Przeprowadźmy zatem krótką dywagację. Załóżmy, że użytkownik #1 wchodzi na stronę, stwierdza, że nie ma cache, lub jest nieświeży, wówczas przechodzi do połączenia się z bazą danych i zaczyna zbierać dane. W tym samym czasie, zanim użytkownikowi #1 zostaną zwrócone dane wchodzi użytkownik #2, który stwierdza, że nie ma cache, bo użytkownik #1 jeszcze nie zebrał danych, postanawia połączyć się z bazą i zrobić to samo, co użytkownik #1, powtarzając niepotrzebnie czynność i dodatkowo obciążając bazę. Można by iść dalej i wprowadzić <em>n</em> użytkowników, którzy powtarzają czynność, dopóki dane nie pojawią się w cache i kolejni użytkownicy będą z niego korzystać. Co się stanie natomiast, gdy kolejka tak narośnie, że użytkownikowi #1 zabraknie zasobów systemowych, aby ukończyć proces zbierania danych, co spowoduje, że pozostałym też? Kolejka będzie wydłużała się w nieskończoność, póki system operacyjny nie podejmie żadnych działań (np. odłączy bazę danych, lub po prostu wyłączy serwer, np. w <a href="http://pl.wikipedia.org/wiki/IIS" rel="nofollow">IIS7</a> wyłączy cały application pool). Aby doszło do tej kolizji nie jest potrzebne wcale natężenie użytkowników, serwer może akurat np. zajmować się wysyłką maili lub nieoptymalnie zrobionym procesem, który zajmuje zasoby, a w tym czasie wejdzie tylko pięciu użytkowników.</p>
<p>Parę linijek kodu, a ile nieszczęść.</p>
<h2>Pojęcie semafora.</h2>
<blockquote><p>Semafor w informatyce &#8211; jest chronioną zmienną lub abstrakcyjnym typem danych, który stanowi klasyczną metodę kontroli dostępu przez wiele procesów do wspólnego zasobu w środowisku programowania równoległego.</p></blockquote>
<p>Więcej na temat semaforów na <a href="http://pl.wikipedia.org/wiki/Semafor_(informatyka)" rel="nofollow">Wikipedii</a>, bądź w <em>Podstawy informatyki / Stefan Węgrzyn. &#8211; Warszawa : Państwowe Wydawnictwo Naukowe, 1982</em>.</p>
<h2>Podejście do problemu.</h2>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #339933;">&lt;</span> ?php
<span style="color: #000088;">$oCache</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Cache<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">expired</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">3600</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span> <span style="color: #339933;">!</span><span style="color: #990000;">is_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aData</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">savePrepare</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// stawiamy semafor</span>
&nbsp;
  <span style="color: #000088;">$aData</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oModel</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">GetSomething</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$oCache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">save</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aData</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// metoda save() może (nie musi) od razu zwolnić semafor, gdy próba zapisu się zakończy</span>
  <span style="color: #666666; font-style: italic;">// jeżeli metoda save() nie zwalnia zasobu, możemy np. użyć:</span>
  <span style="color: #666666; font-style: italic;">// $oCache-&gt;saveFinalize();</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// $aData przechowuje nasze dane do użytku</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Rozwiązaniem jest zastosowanie <strong>semafora</strong> blokującego dostęp do zasobu (w tym przypadku abstrakcyjnie &#8220;cache&#8221;, mniej abstrakcyjnie może być to plik na dysku, przestrzeń w pamięci operacyjnej, rekord w bazie danych, cokolwiek, co cache przerzymuje). Dla wartości semafora = 1 zasób jest wolny (nieużywany, jest 1 cache), gdy jest mniejszy/równy 0 zasób jest zajęty, ktoś z niego &#8220;korzysta&#8221;. Zajętość zasobu powinna być sprawdzana przy próbie odczytu. Dopóki zasób nie zostanie zwolniony, nie będzie można określić, czy są dane w cache. Jeżeli nie można określić, czy dane są w cache, należy zaczekać na zwolnienie zasobu.</p>
<p>Teraz nasze rozwiązanie nie dopuści do przytoczonej w powyższym przykładzie sytuacji. Zanim cache nie zostanie odblokowany po próbie zapisu, nie uzyskamy odczytu, czekając na niego i nie przechodząc w skrypcie nigdzie dalej.</p>
<p>Gdy <code>save()</code> się nie powiedzie? Można zastosować timeouty odczytu na <code>load()</code>. Wówczas złapalibyśmy wyjątek i przeszli dalej do realizacji zapisu, tak, jakby semafora nie było.</p>
<h2>Implementacja.</h2>
<p>Do swoich kodów podchodzę jak najbardziej abstrakcyjnie (tutaj idealnie nada się <a href="http://pl.wikipedia.org/wiki/Fabryka_abstrakcyjna_(wzorzec_projektowy)">wzorzec fabryki</a>), zatem stworzyłem klasę <em>Cache</em>, która obsługuje &#8216;silniki&#8217; implementujące interfejs <em>Cache_Engine</em>. Jednym z nich jest silnik <em>Cache_Engine_File</em>, który wykorzystuje pliki na dysku do składowania cache.</p>
<p>Najprostszym semaforem dla plików jest funkcja <code><a href="http://php.net/flock">flock()</a></code> (gotowe, sprawdzone rozwiązanie, w dodatku na poziomie systemu plików, nic tylko implementować). Sprawa wygląda bardzo prosto, dopóki nie zwolnimy flagi <code>LOCK_EX</code> po jej założeniu, ludzie nie będą czytali z pliku, czekając na zwolnienie dostępu. Ktoś powie: truizm, blokować pliki powinno się przed wykonywaniem na nich operacji. Tak. Ale grunt, w którym miejscu to zablokowanie nastąpi. Wykorzystujemy blokowanie do wyższego celu.</p>
<p>Wg. dokumentacji nie można polegać na <code>flock()</code> w przypadku Windows98 oraz systemów FAT32. Zbyt dużym poziomem abstrakcji jest dla mnie stawianie serwisu na pamięci flash lub Win98, ale faktycznie, najprostsza pamięć flash z systemem FAT32 może się czasem zdarzyć w serwerowniach i nie jest to wcale taki głupi pomysł. Co wtedy? Jako semafor możemy stworzyć plik z suffiksem <em>.lock</em> obok tworzonego pliku cache. Gdy plik istnieje oznacza to, że cache jest zablokowany, jeżeli nie &#8211; jest wolny. Czekamy tak długo, aż zostanie usunięty plik <em>.lock</em>.</p>
<h2>Przykładowy kod źródłowy.</h2>
<p>Przykładowy kod źródłowy obsługuje <em>Cache_Engine_File</em> oraz <em>Cache_Engine_Filelock</em>, gdzie w drugim przypadku można klasy użyć spokojnie na partycjach FAT32. Kod jest przykładowy, dlatego nie obsługuje m.in. zagnieżdżania plików w katalogach, usuwanie cache&#8217;u itd, zaimplementowałem tylko zapis i odczyt.</p>
<p>Klasy zostały napisane tak, aby zgłaszane przez nie błędy były <a href="http://www.php.net/manual/en/spl.exceptions.php">zgodnie z ideologią hierarchiczną Exceptions w PHP</a>, przy okazji zapraszam do lektury wpisu <a href="http://www.zyxist.com/pokaz.php/wyjatki_w_php">&#8220;Wyjątki w PHP&#8221;</a> autorstwa Tomasza Jędrzejewskiego (Zyxits).</p>
<ul>
<li><a href="http://athlan.pl/code/lib-cache/Cache">Klasa Cache</a>, Cache_Exception, abstrakcja Cache_Engine</li>
<li><a href="http://athlan.pl/code/lib-cache/Cache/Engine/File">Klasa Cache_Engine_File</a>, która bazuje na plikach</li>
<li><a href="http://athlan.pl/code/lib-cache/Cache/Engine/Filelock">Klasa Cache_Engine_Filelock</a>, która bazuje na plikach + tworzy pliki <em>.lock</em></li>
</ul>
<p>Przykładowe czekanie na zwolnienie pliku <em>.lock</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #339933;">&lt;</span> ?php
&nbsp;
<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000000; font-weight: bold;">function</span> _waitUnlock<span style="color: #009900;">&#40;</span><span style="color: #000088;">$iWaitTimeout</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$iWaitTimeout</span><span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#123;</span>
    try
    <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// quick first check</span>
      <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">is_file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_path<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'lock'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
      <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// wait for unlock file</span>
        <span style="color: #000088;">$iWaitTimeout</span> <span style="color: #339933;">/=</span> <span style="color: #cc66cc;">1000000</span><span style="color: #339933;">;</span>
        <span style="color: #000088;">$iLockTime</span> <span style="color: #339933;">=</span> <span style="color: #990000;">microtime</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000088;">$bLockWait</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// wait for the file</span>
        try
        <span style="color: #009900;">&#123;</span>
          <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">is_file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_path<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'lock'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
          <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$iLockWaitDelta</span> <span style="color: #339933;">=</span> <span style="color: #990000;">microtime</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$iLockTime</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$iLockWaitDelta</span> <span style="color: #339933;">&gt;</span> <span style="color: #000088;">$iWaitTimeout</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000088;">$iWaitTimeout</span> <span style="color: #339933;">!==</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span>
              <span style="color: #009900;">&#123;</span> <span style="color: #000088;">$bLockWait</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span> <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #990000;">usleep</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">rand</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">999</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #666666; font-style: italic;">// cache lock path does not exists</span>
        catch<span style="color: #009900;">&#40;</span>Cache_Exception_Runtime <span style="color: #000088;">$oE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000088;">$bLockWait</span><span style="color: #009900;">&#41;</span>
          <span style="color: #b1b100;">throw</span> <span style="color: #000000; font-weight: bold;">new</span> Cache_Exception_Runtime<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Unable to access cache, it is totally locked, after &quot;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$iWaitTimeout</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot; s.'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #666666; font-style: italic;">// cache lock path does not exists</span>
    catch<span style="color: #009900;">&#40;</span>Cache_Exception_Runtime <span style="color: #000088;">$oE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #b1b100;">else</span>
  <span style="color: #009900;">&#123;</span>
    try
    <span style="color: #009900;">&#123;</span>
      <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">is_file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_path<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'lock'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
        <span style="color: #b1b100;">throw</span> <span style="color: #000000; font-weight: bold;">new</span> Cache_Exception_Runtime<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Unable to access cache, it is currently locked, after &quot;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$iWaitTimeout</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot; s.'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #666666; font-style: italic;">// cache path does not exists</span>
    catch<span style="color: #009900;">&#40;</span>Cache_Exception_Runtime <span style="color: #000088;">$oE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/php-cache-semafory/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Atak XSS na $_SERVER[&#039;HTTP_X_FORWARDED_FOR&#039;]</title>
		<link>http://athlan.pl/atak-xss-server-http-x-forwarded-for/</link>
		<comments>http://athlan.pl/atak-xss-server-http-x-forwarded-for/#comments</comments>
		<pubDate>Sat, 28 May 2011 12:40:13 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=706</guid>
		<description><![CDATA[Dziś bardzo krótko, bez zbędnych dywagacji, czyli tylko i wyłącznie o tablicy $_SERVER. Dbając o bezpieczeństwo aplikacji webowych zwraca się uwagę na wiele czynników, jakimi są SQL injections, przechwytywanie nieprawidłowych parametrów, uogólniające zapytania przepuszczające maskę % w LIKE zapytaniu do baz, XSS&#8216;y w $_POST, $_GET. I finalnie&#8230; wiele osób zapomina (a jeszcze więcej nie jest [...]]]></description>
			<content:encoded><![CDATA[<p>Dziś bardzo krótko, bez zbędnych dywagacji, czyli tylko i wyłącznie o tablicy <code>$_SERVER</code>. Dbając o bezpieczeństwo aplikacji webowych zwraca się uwagę na wiele czynników, jakimi są <em>SQL injections</em>, przechwytywanie nieprawidłowych parametrów, uogólniające zapytania przepuszczające maskę % w <em>LIKE</em> zapytaniu do baz, <em><acronym title="Cross-site scripting">XSS</acronym></em>&#8216;y w <code>$_POST</code>, <code>$_GET</code>.</p>
<p>I finalnie&#8230; wiele osób zapomina (<u>a jeszcze więcej nie jest tego świadom</u>) o możliwości wstrzyknięcia szkodliwych danych w <code>$_SERVER['HTTP_X_FORWARDED_FOR']</code>;. <u>Konsekwencje są oczywiście katastrofalne</u>.</p>
<p>O ile sama walidacja jest rzeczą wtórną, diabeł tkwi w trzech szczegółach:</p>
<ol>
<li>Rzecz trywialna, ale pamiętajmy, że w naturalnym procesie użytkowania przeglądarki, <strong>w nagłówku może zostać zwrócony nie tylko jeden adres IP</strong>, a kilka oddzielonych przecinkiem, w tym <em>localhost</em>&#8216;y (<a rel="nofollow" href="http://en.wikipedia.org/wiki/X-Forwarded-For">standard nagłówka <code>X-Forwarded-For</code></a>).</li>
<li><strong>Wstrzyknięcie Javascriptów</strong> jest możliwe, ale notabene najmniej szkodliwe, bo do spreparowania nagłówka potrzebny jest bardziej zaawansowany proces (dajmy na to Data Tamping, który przedstawię poniżej), np. niż wklejenie syfu w linku/obrazku i przesłanie go komuś przez komunikator, żeby wykraść jego ciasteczka sesyjne <code>document.cookie</code> i przesłać je sobie na serwer w dowolny sposób, <strong>zatem atakowi nie ulegną osoby trzecie</strong>.</li>
<li><strong>Niepoprawność danych</strong>, które można zmanipulować, jest chyba rzeczą oczywistą: nieprzepuszczenie takich danych przez filtry może skutkować złymi wartościami zwracanymi np. przez <code><a href="http://php.net/ip2long">ip2long()</a></code> i zapis w zupełności nieprzydatnych nam później danych do bazy.</li>
<li>&#8230; <strong>ale największe nieprzyjemności</strong> możemy mieć przez spreparowanie lewych zapytań do baz danych, o ile nie używamy sprawdzonych ORM lub czegokolwiek, co pomaga nam filtrować wartości do niej przekazywane i używane w warunkach zapytań (data binding).</li>
</ol>
<p>Przykład tampingu danych, żeby spreparować niepożądane efekty.</p>
<p>Mamy bardzo prosty, niebezpieczny kod funkcji, która pobiera pierwszy adres na liście adresów oddzielonych przecinkami z <code>$_SERVER['HTTP_X_FORWARDED_FOR']</code> o ile istnieje, natomiast w przeciwnym wypadku <code>$_SERVER['REMOTE_ADDR']</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #339933;">&lt;</span> ?php
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> getUserIp<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'HTTP_X_FORWARDED_FOR'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #990000;">trim</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">current</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'HTTP_X_FORWARDED_FOR'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #b1b100;">return</span> <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'REMOTE_ADDR'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000088;">$sUserIP</span> <span style="color: #339933;">=</span> getUserIp<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">'Hi &quot;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$sUserIP</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot;!'</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// first bug while display.</span>
<span style="color: #990000;">var_dump</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">ip2long</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sUserIP</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// second bug while transforming data.</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p><strong>Pora na przykład manipulacji takich danych.</strong></p>
<ul>
<li>Będziemy używać <a href="https://addons.mozilla.org/en-us/firefox/addon/tamper-data/">Tamper Data</a> dla Firefox&#8217;a.<br />Dość popularny wśród developerów addon do Firefox&#8217;a, pozwala zmodyfikować dane <code>$_POST</code>, <code>$_GET</code>, <code>$_COOKIE</code>, nagłówki, &#8220;w locie żądania&#8221; etc.</li>
<li>Po instalacji w menu <em>Narzędzia</em> pojawi się pozycja <em>Dane Tamper</em>, która uruchamia okienko do podsłuchiwania żądań. Po kliknięciu <em>Rozpocznij</em> podsłuchujemy wszystkie wychodzące żądania z naszej przeglądarki. Każde żądanie nie zostanie przepuszczone, dopóki go nie zmanipulujemy klikając <em>Tamper</em>, lub przepuścimy dalej klikając <em>Wyślij</em>.</li>
<li>Jeżeli zdecydujemy się Tamper&#8217;ować żądanie, naszym oczom ukaże się okno z parametrami. Klikamy prawym przyciskiem myszy na listę parametrów, wybieramy Dodaj i wpisujemy nasz przykładowy, brzydki dla aplikacji nagłówek:<br />
<code>X_FORWARDED_FOR=&lt;script&gt;alert('Test.')&lt;/script&gt;</code></li>
</ul>
<p>Naszym oczom ukazują się co najmniej dwa błędy. Pierwszy to błąd prezentacji danych, który wykorzystuje <code>&lt;script&gt;</code>. O ile nie musimy się tym przejmować, bo naturalnie takie żądania nie są tak łatwo wysyłane, użytkownik nie może paść ofiarą ataku przez kliknięcie w link, który np. ukradnie mu ciasteczka. Dane nagłówkowe nie są w stanie być zmodyfikowane poprzez kliknięcie w link, podobnie jak z danymi <code>$_POST</code> (oczywiście mówimy o przypadkach trywialnych, bez javascript&#8217;owych wymuszanych submitów targetowanych do np. ramek).</p>
<p>Znacznie poważniejszym błędem jest konsekwencja wadliwego formatu danych, które nasza funkcja bagatelizuje. Po pierwsze mamy fałszywe dane zwracane przez <code><a href="http://php.net/ip2long">ip2long()</a></code>, po drugie kto powiedział, że właśnie z tej funkcji korzystamy, a nie zapisujemy danych plain&#8217;em i nie bindujemy pofiltrowanych danych lub instrukcji warunkowych zapytania przez np. sprawdzony ORM.</p>
<p><strong>Rozwiązanie problemu.</strong></p>
<p>Edit: Jak słusznie zauważył Zyx, zapomniałem o tym wspomnieć, że skoro mogą znaleźć się tam dowolne dane przesłane od użytkownika, <strong>nie należy tego pola traktować jako wyznacznik, że jest to numer jego IP</strong>, <u>jest ono bezużyteczne i powoduje potencjalną lukę</u>. Poza zabezpieczeniami to podstawowy argument, żeby o polu zapomnieć i używać <code>$_SERVER['REMOTE_ADDR']</code>.</p>
<p>Po pierwsze funkcja powinna sprawdzać dane wejściowe chociażby <code><a href="http://php.net/preg-match">preg_match()</a></code> lub konwersją do <code><a href="http://php.net/ip2long">ip2long()</a></code> i (jeżeli jest taka potrzeba) spowrotem do <code><a href="http://php.net/long2ip">long2ip()</a></code>. Dwa, pamiętajmy, że w X_FORWARDED_FOR znajdują się śmieci, adresy lokalne sieci,  itd., które należy pominąć przy wyborze adresu z listy po przecinku.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/atak-xss-server-http-x-forwarded-for/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Chmura tagów Tagcloud w PHP</title>
		<link>http://athlan.pl/chmura-tagow-tagcloud-php/</link>
		<comments>http://athlan.pl/chmura-tagow-tagcloud-php/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 19:12:21 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[tagcloud]]></category>
		<category><![CDATA[tagi]]></category>
		<category><![CDATA[tags]]></category>
		<category><![CDATA[web2.0]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=439</guid>
		<description><![CDATA[Ostatnio byłem zobligowany napisać nową klasę tagów do mojego projektu Sypacz.pl, która de facto zachowała stare API, lecz rozszerzyła swoje funkcjonalności, więc w kodzie projektu nie było wielu zmian. Wena spowodowała to, że zacząłem pisać kod od zera. Cały problem polega na tym, aby napisać na tyle elastyczną klasę tagów, która przyjmie nam zestaw danych, [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnio byłem zobligowany napisać nową klasę tagów do mojego projektu <a href="http://sypacz.pl">Sypacz.pl</a>, która de facto zachowała stare API, lecz rozszerzyła swoje funkcjonalności, więc w kodzie projektu nie było wielu zmian. Wena spowodowała to, że zacząłem pisać kod od zera.</p>
<p>Cały problem polega na tym, aby napisać na tyle elastyczną klasę tagów, która przyjmie nam zestaw danych, a następnie zaprezentować ją w formie chmury, czym zaopiekuje się arkusz stylów CSS:</p>
<p><code>Nazwa tagu =&gt; Ilość występowań</code></p>
<p>Parę osób mnie pytało, jak wyciągnąć takie informacje z bazy danych:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">SELECT tag_name<span style="color: #339933;">,</span> <span style="color: #990000;">COUNT</span><span style="color: #009900;">&#40;</span>tag_name<span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">AS</span> tag_times FROM tags GROUP BY tag_name ORDER BY tag_times LIMIT <span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">50</span></pre></div></div>

<p style="border: 2px solid red; padding: 15px;"><strong><span style="color: #ff0000;">Uwaga!</span></strong> Zaprezentowane wyżej zapytanie jest przykładowe, nieoptymalne, a jedynie służące do testowania chmur tagów na małych, testowych bazach danych. <strong>Optymalna implementacja struktur tagów w bazie danych</strong> dla większych projektów <strong>została opisana we <a href="/mysql-tags/">wpisie MySQL tags</a></strong>.</p>
<p>Wykorzystałem obiekt <a href="http://athlan.pl/code/Attribute.Class">Vframe_Attribute</a>, aby ustandaryzować komponent względem pozostałych w moim frameworku. Jeżeli ktoś nie chce używać obiektu Attribute, może w prosty sposób przekształcić klasę tagów, otrzymując ten sam efekt, deklarując tylko atrybut chroniony <code>protected $_aAttributes = array();</code>. Temat chmury tagów wydaje mi się na tyle trywialny, że nie ma się co nad nim zbyt wiele rozwodzić, zamieszczę tylko klasę i opiszę krótko w przykładach jej możliwości.</p>
<ul>
<li><a href="http://athlan.pl/code/Tagcloud.Class">Vframe_Tagcloud</a> &#8211; klasa tagów,</li>
<li><a href="http://athlan.pl/code/Attribute.Class"> Vframe_Attribute</a> &#8211; pomocnicza klasa atrybutów dla stosu $_aAttributes, dziedziczenie można usunąć i zadeklarować atrybut samemu.</li>
</ul>
<p>Aby stworzyć nowy obiekt tagów, po prostu wywołujemy konstruktor:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$oCloud</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Vframe_Tagcloud<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Konstruktor nie przyjmuje żadnych argumentów, wiec możemy od razu przejść do podawania obiektowi tagów. W tym miejscu warto nadmienić, że każdy znak jest rozróżniany (ze względów elastycznych), więc jeżeli chcesz, aby Nazwatagu oraz nazwatagu były rozpoznawane jako jeden klucz, wypadałoby użyć funkcji <a href="http://php.net/strtolower">strtolower</a> lub <a href="http://php.net/mb_strtolower">mb_strtolower</a> (dla Multibyte Strings):</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aDataTags</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$iKey</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#41;</span>
  <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>add<span style="color: #009900;">&#40;</span><span style="color: #990000;">strtolower</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aRow</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'tag_name'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'tag_times'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Najistotniejszą częścią klasy jest sposób renderowania chmury, które może działać w dwóch trybach:</p>
<ul>
<li><strong>Tryb prosty</strong> zwraca nam nazwę tagu oraz jego wagę po przeliczeniu w formie liczby.</li>
<li><strong>Tryb zaawansowany</strong> zwraca nam nazwę tagu oraz tablicę z danymi:
<ul>
<li><code>level</code> &#8211; waga tagu po przeliczeniu,</li>
<li><code>count</code> &#8211; ilość występowań, taka jaką podaliśmy,</li>
<li><code>count_percentage</code> &#8211; informacja, w jakiej procentowej części ilości występowań znajduje się tag, przyjmując za 100% tag, który występuje najczęściej.</li>
</ul>
</li>
</ul>
<p>Aby w prosty sposób wyrenderować chmurę tagów, używamy poniższego przykładu:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$aDataTagsRender</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>render<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Najczęściej używa się trybu prostego. Oba tryby są dalej rozbudowane, bowiem <strong>mamy możliwość zdefiniowania zakresu i dokładności wag tagów</strong>. <span style="text-decoration: underline;">Domyślnie wagi tagów zawierają się pomiędzy 1, a 10</span>. Możemy na przykład przyjąć, że najmniejszą wagą jest liczba 3, największą 5, a precyzja wag tagów to 2 miejsca po przecinku:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$aDataTagsRender</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>render<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">3</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">5</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>W celu uniknięcia precyzji po przecinku (chcemy otrzymać liczby całkowite), ustawimy precyzję na 0.</p>
<p>Aby wywołać tryb zaawansowany, musimy podać 4 argument dla metody <code>render()</code> i ustawić go na <code>true</code>.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$aDataTagsRender</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>render<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">5</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Warto w tym miejscu nadmienić, że tagi mogą nie być posortowane alfabetycznie (co ma miejsce podczas tworzenia chmury tagów). Wystarczy wywołać funkcję <code>ksort</code> (key sort).</p>
<p>Finalny przykład używania klasy tagów, celem wywołania klas CSS level_X, gdzie X to liczba całkowita z zakresu od 1 do 10, resztę robi CSS (kolorowanie, nakładanie rozmiaru):</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">count</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aDataTags</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
<span style="color: #000088;">$oCloud</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Vframe_Tagcloud<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aDataTags</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$iKey</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#41;</span>
  <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>add<span style="color: #009900;">&#40;</span><span style="color: #990000;">strtolower</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aRow</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'tag_name'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'tag_times'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$aDataTags</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>render<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">10</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">ksort</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aDataTags</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$aDataTags</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$sTag</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$iTag</span><span style="color: #009900;">&#41;</span>
  <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">'&lt;a class=&quot;level_'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$iTag</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot; href=&quot;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>route<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'tag'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$sTag</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&quot;&gt;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$sTag</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'&lt;/a&gt;'</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/chmura-tagow-tagcloud-php/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>MVC &#8211; Model</title>
		<link>http://athlan.pl/mvc-model/</link>
		<comments>http://athlan.pl/mvc-model/#comments</comments>
		<pubDate>Mon, 09 Mar 2009 15:21:19 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[batabae]]></category>
		<category><![CDATA[baza danych]]></category>
		<category><![CDATA[dane]]></category>
		<category><![CDATA[last.fm]]></category>
		<category><![CDATA[model]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[wzorce projektowe]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=258</guid>
		<description><![CDATA[Powstało masę artykułów na temat MVC, temat staje się naprawdę oklepany. Postanowiłem zebrać wszystkie informacje w jedno miejsce i streścić je w jednym artykule uzupełniając go o informacje, które nabyłem z własnego doświadczenia oraz zwracając uwagę na najistotniejsze informacje. Czym jest model Model to jedna z warstw wzorca projektowego MVC, który odpowiada logikę biznesową, czyli [...]]]></description>
			<content:encoded><![CDATA[<p>Powstało <a href="http://phpedia.pl/wiki/MVC">masę artykułów</a> na temat <strong>MVC</strong>, temat staje się naprawdę <em>oklepany</em>. Postanowiłem zebrać wszystkie informacje w jedno miejsce i streścić je w jednym artykule uzupełniając go o informacje, które nabyłem z własnego doświadczenia oraz zwracając uwagę na najistotniejsze informacje.</p>
<p><strong>Czym jest model</strong></p>
<p><strong>Model </strong>to jedna z warstw wzorca projektowego <strong>MVC</strong>, który odpowiada logikę biznesową, czyli pozyskiwanie oraz modelowanie danych pozyskanych ze źródła danych. Na samym wstępie brzmi to bardzo abstrakcyjnie. W myśl architektury MVC, <span style="text-decoration: underline;">dostęp</span> do modelu <span style="text-decoration: underline;">powinien mieć tylko kontroler</span>, a w żadnym wypadku widok. Dodatkowo model musi pobrać i modelować dane w taki sposób, <span style="text-decoration: underline;">aby można było go ewentualnie wymienić bez jakiejkolwiek ingerencji w kontroler</span>, a co za tym idzie – widok. Niezależnie od tego, z jakiego źródła informacji korzysta (pliki tekstowe, bazy danych, pliki XML)  kontroler powinien otrzymać maksymalnie zbliżone dane podczas wymiany źródła informacji.</p>
<p style="text-align: center;"><a href="http://athlan.pl/wp-content/uploads/mvc-model.png"><img class="aligncenter size-medium wp-image-259" style="border: 0pt none;" title="mvc-model" src="http://athlan.pl/wp-content/uploads/mvc-model-265x300.png" alt="mvc-model" width="265" height="300" /></a></p>
<p><strong>Model != baza danych</strong></p>
<p>Często spotykam się z definicją modelu jako źródłem połączenia i wykonywania zapytań do serwera bazy danych. Otóż nie jest to prawdą. Według ideologii MVC model powinien być jedynie pośrednikiem między warstwą aplikacji przeznaczoną do połączenia do bazy danych, wykonywania zapytań itp., a kontrolerem. Dodatkowo powinien pomóc kontrolerowi w zbudowaniu zapytania do źródła informacji (pobranie danych na podstawie kryteriów), zmodelować je i zwrócić. Dlaczego model nie jest połączeniem do bazy danych? Jeżeli model potraktujemy jako pośrednika między kontrolerem a źródłem danych, ma on prawo wybrać dowolny sposób uzyskania żądanych informacji. Wcale nie oznacza to, że model musi używać baz danych, ale może użyć plików XML lub API udostępniane przez konkretny serwis (np. YouTube)</p>
<p><strong>Wymienialność modeli i modelowanie danych</strong></p>
<p>Modelowanie informacji jest to dostosowanie ich do użytku przez kontroler. Zazwyczaj jest to przekazywanie informacji w postaci tablic, wartości logicznych, liczb i ciągów znaków. Przykładem może być pobieranie informacji z bazy danych. Kontroler de facto nie wie skąd są pobierane dane, wie to tylko model, otrzymuje suche informacje. Jak rozumieć modelowanie danych przy projektowaniu aplikacji? Wyobraźmy sobie sytuację, że zmieniamy źródło informacji z bazy danych na pliki XML. W tym przypadku kontroler <span style="text-decoration: underline;">powinien otrzymać rekordy danych jako tablica o tych samych kluczach i tych samych typach danych</span>, jak miało to miejsce przy używaniu bazy danych. <span style="text-decoration: underline;">Wymiana modelu odbywa się bez ingerowania w kontroler</span>.</p>
<p><strong>Przykłady modeli</strong></p>
<p>Najpopularniejszym sposobem pozyskania informacji jest połączenie do bazy danych i pobieranie (reprezentowanie) ich na różnoraki sposób. Doskonale wyjaśnia to tekst znajdujący się w wikipedii:</p>
<p style="padding-left: 30px;">Frameworki MVC do operacji na bazach danych używają modeli i mapowania relacyjno-obiektowego, <a class="mw-redirect" title="ORM" href="http://pl.wikipedia.org/wiki/ORM">ORM</a> (ang. <em>object-relationship mapping</em>) &#8211; w Railsach jest to ActiveRecord, w Catalyscie np. DBIx::Class, a framework <a title="Spring Framework" href="http://pl.wikipedia.org/wiki/Spring_Framework">Spring</a> w Javie używa Hibernate. Zwykle jest też możliwe użycie baz danych przez bezpośrednie zapytania <a title="SQL" href="http://pl.wikipedia.org/wiki/SQL">SQL</a>. Użycie modeli upraszcza typowe operacje &#8211; wyświetlanie ze stronicowaniem, edycję danych, a także uniezależnia od konkretnego typu bazy danych.</p>
<p>Posiadam przykład od siebie. Źródłem danych jest API serwisu Last.fm:</p>
<ul>
<li><a href="http://athlan.pl/code/ModelLastfmApi">Model</a> informacji o utworze.</li>
<li><a href="http://athlan.pl/code/ModelLastfmApiAbstract">Klasa abstrakcyjna modelu</a> opartego o API Last.fm (oparty na klasie bazowej modelu frameworka).</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mvc-model/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Zintegrowane logowanie cms z forum</title>
		<link>http://athlan.pl/zintegrowane-logowanie-forum/</link>
		<comments>http://athlan.pl/zintegrowane-logowanie-forum/#comments</comments>
		<pubDate>Fri, 06 Feb 2009 09:42:35 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[autoryzacja]]></category>
		<category><![CDATA[cms]]></category>
		<category><![CDATA[forum]]></category>
		<category><![CDATA[logowanie]]></category>
		<category><![CDATA[phpBB]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=214</guid>
		<description><![CDATA[Projektowałem wiele serwisów, które miały zintegrowane z forum komponenty takie jak: rejestracja, przypomnienie hasła, zmiana hasła, nicku lub adresu email, usunięcie konta. Wówczas nie było żadnego problemu &#8211; wystarczyło wszystkie te akcje z forum przekierować na URL&#8217;e obsługiwane przez CMS, który zajmował się zmianami  tabelach forum. Dlaczego przekierować? Jeżeli ktoś rejestruje się w serwisie, jest [...]]]></description>
			<content:encoded><![CDATA[<p>Projektowałem wiele serwisów, które miały zintegrowane z forum komponenty takie jak:</p>
<ul>
<li>rejestracja,</li>
<li>przypomnienie hasła,</li>
<li>zmiana hasła, nicku lub adresu email,</li>
<li>usunięcie konta.</li>
</ul>
<p>Wówczas nie było żadnego problemu &#8211; wystarczyło wszystkie te akcje z forum przekierować na URL&#8217;e obsługiwane przez CMS, który zajmował się zmianami  tabelach forum. Dlaczego przekierować? Jeżeli ktoś rejestruje się w serwisie, jest zarejestrowany na forum, natomiast, gdy rejestruje się na forum, nie jest rejestrowany w serwisie. To CMS integrujemy z forum, a nie forum z CMS&#8217;em (chyba, że zamierzamy inaczej, wtedy na odwrót).</p>
<p>Ostatnio klient zażyczył sobie, żeby zintegrowane było również logowanie. Nie najlepiej widzi mi się implementacja systemu autoryzacji z forum w CMS&#8217;ie, więc poszedłem &#8220;na łatwiznę&#8221;, bowiem miałem do czynienia z <a href="http://przemo.org/phpBB2/">phpBB</a>. Do osiągnięcia celu postanowiłem wykonać dwa kroki:</p>
<ol>
<li>wysłać żądanie POST do forum na adres logowania z wypełnionymi polami POST z formularza logowania w CMS&#8217;ie,</li>
<li>przechwycić wysłane przez forum ciasteczka i przekazać je użytkownikowi.</li>
</ol>
<p>Do połączenia się z forum via http użyłem <a href="http://pl.php.net/HttpRequest">HttpRequest</a>. Wyszło z tego <a href="http://athlan.pl/code/ForumAuth">parę linijek kodu</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/zintegrowane-logowanie-forum/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Wzorzec projektowy Registry w PHP</title>
		<link>http://athlan.pl/wzorzec-registry-php/</link>
		<comments>http://athlan.pl/wzorzec-registry-php/#comments</comments>
		<pubDate>Sat, 17 Jan 2009 22:22:54 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[registry]]></category>
		<category><![CDATA[wzorce projektowe]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=195</guid>
		<description><![CDATA[Registry to wzorzec projektowy, który ma za zadanie przechowywać i udostępniać dane w obrębie aplikacji. Implementacja wzorca zastępuje globalny zasięg wartości zarejestrowanych w przestrzeni klasy. Różnicą globalnego zasięgu zmiennych oraz wartości ujętych w Registry jest to, że można je ściśle kontrolować (dostęp w aplikacji itd.). W tej publikacji przedstawię jedną z najprostszego wykorzystania wzorca Registry. [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Registry</strong> to <a href="http://phpedia.pl/wiki/Wzorce_projektowe">wzorzec projektowy</a>, który ma za zadanie przechowywać i udostępniać dane w obrębie aplikacji. Implementacja wzorca zastępuje globalny zasięg wartości zarejestrowanych w przestrzeni klasy. Różnicą globalnego zasięgu zmiennych oraz wartości ujętych w Registry jest to, że można je ściśle kontrolować (dostęp w aplikacji itd.). W tej publikacji przedstawię jedną z najprostszego wykorzystania wzorca Registry.</p>
<p>Głównym założeniem Registry jest globalny zasięg (pomijając już zabezpieczenia dostępowe, którymi się nie zajmujemy). Język PHP od wersji 5 obsługuje statyczne <em>static</em> wywołania metod klas, czyniąc tym samym ich globalny zasięg wraz z połączeniem ze słowem kluczowym <em>public</em>. Dla wygody &#8211; nie trzeba tworzyć instancji klasy, więc wywołanie jest bardzo proste i nie zajmuje dużo miejsca w naszym kodzie.</p>
<p>Podstawowymi funkcjonalnościami Registry będzie:</p>
<ul>
<li>dodawanie i usuwanie,</li>
<li>pobieranie</li>
</ul>
<p>&#8230;zmiennych z rejestru. Pojęcie <em>zmienna</em> jest bardzo względne. Przechowywać w rejestrze możemy praktycznie wszystkie typy zmiennych dostępnych w PHP, włączając w to typ <em><a href="http://pl.php.net/manual/pl/resource.php">resource</a> (zasoby)</em>. Registry to nic innego, jak przechowywanie zmiennych w przestrzeni jednej klasy, więc nie mamy wobec tego żadnych ograniczeń.</p>
<ul>
<li><a href="http://athlan.pl/code/RegistrySimple">Przykładowy kod najprostszej implementacji wzorca Registry</a>.</li>
<li><a href="http://athlan.pl/code/RegistrySimpleUsage">Użycie powyższej klasy <em>RegistrySimple</em></a>.</li>
</ul>
<p>Zajmiemy się teraz bardziej rozbudowanym przykładem wzorca. Wykonamy następujące operacje:</p>
<ol>
<li>Nowa klasa <em>RegistryAdvenced</em> będzie dziedziczyła z <em>RegistrySimple</em> na potrzeby metody <em>Registry()</em>.</li>
<li>Zaimplementujemy interfejsy:
<ul>
<li><a href="http://pl.php.net/manual/pl/class.iterator.php">Iterator</a></li>
<li><a href="http://pl.php.net/manual/pl/class.arrayaccess.php">ArrayAccess</a> (na potrzeby poruszania się po instancji jak po tablicy)</li>
<li><a href="http://pl.php.net/manual/pl/class.countable.php">Countable</a> (aby łatwo otrzymać liczbę przechowywanych danych)</li>
<li><a href="http://pl.php.net/manual/pl/class.serializable.php">Serializable</a> (żeby można było <a href="http://pl.php.net/manual/pl/function.serialize.php">zaserializować</a> i <a href="http://pl.php.net/manual/pl/function.unserialize.php">odserializować</a> instancję obiektu)</li>
</ul>
</li>
<li>Dodamy prywatny konstruktor, skorzystamy ze wzorca <a href="http://phpedia.pl/wiki/Singleton">Singleton</a>.</li>
</ol>
<p><a href="http://athlan.pl/code/RegistryExtended">Gotowy kod klasy <em>RegistryAdvenced</em></a>.</p>
<p>Interpretacja i rozwijanie swojego wzorca Registry jest szeroka, można na przykład zastosowac przestrzenie rejestrów, tj. tworzyć wiele rejestrów na podstawie ich instancji. Ile programistów, tyle pomysłów, ale zasada działania nie zmienia się. Zainteresowanych zapraszam do manuala, jak problem został rozwiązany w <a href="http://framework.zend.com/manual/en/zend.registry.html">Zend_Registry</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/wzorzec-registry-php/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Paginacja (stronicowanie) w PHP</title>
		<link>http://athlan.pl/paginacja-php-stronicowanie/</link>
		<comments>http://athlan.pl/paginacja-php-stronicowanie/#comments</comments>
		<pubDate>Sun, 04 Jan 2009 18:03:37 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[mysql limit]]></category>
		<category><![CDATA[pager]]></category>
		<category><![CDATA[paginacja]]></category>
		<category><![CDATA[stronicowanie]]></category>
		<category><![CDATA[strony]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=177</guid>
		<description><![CDATA[Zaczynając swoją przygodę z PHP nie miałem pojęcia jak wykonać paginację newsów, która załamywałaby łańcuchy liczb w momencie, w których chce. Na dzień dzisiejszy postanowiłem napisać swój nowy pager, gdyż ten, który dotychczas używałem przez ostatnie 2 lata nie odpowiadał mi pod trzema względami: nagromadzenie metod, tj. setLimit(), getLimit(), zamiast pojedynczego limit(), brak możliwości ustawienia [...]]]></description>
			<content:encoded><![CDATA[<p>Zaczynając swoją przygodę z PHP nie miałem pojęcia jak wykonać paginację newsów, która załamywałaby łańcuchy liczb w momencie, w których chce. Na dzień dzisiejszy postanowiłem napisać swój nowy pager, gdyż ten, który dotychczas używałem przez ostatnie 2 lata nie odpowiadał mi pod trzema względami:</p>
<ul>
<li>nagromadzenie metod, tj. setLimit(), getLimit(), zamiast pojedynczego limit(),</li>
<li>brak możliwości ustawienia załamywania się łańcucha (domyślnie były to 2 liczby):<br />
<code>1, 2 ... 6, 7, 8 ...  12, 13</code>.</li>
<li>brak możliwości definiowania limitu, ilości elementów oraz aktualnie przeglądanej strony w jednej metodzie, najchętniej w samym konstruktorze.</li>
</ul>
<p>Ostatnio potrzebowałem zwiększyć limit liczb &#8220;z przodu&#8221; i &#8220;tyłu&#8221; oraz &#8220;w środku&#8221;:<br />
<code>1, 2, 3 ... 5, 6, 7, 8, 9 ...  11, 12, 13</code></p>
<p>Dla tych, którzy ciągle szukają komponentu obsługującego paginację prezentuję <a href="http://athlan.pl/code/Vframe_Pagination">Vframe_Pagination</a>.</p>
<p>Implementacja od strony kontrolera (metoda krótka):</p>
<p><code>$oPager = new Vframe_Pagination($iItemsCount, $iLimit, $iCurrentPage);</code></p>
<p>Metoda długa:</p>
<p><code>$oPager = new Vframe_Pagination();<br />
$oPager-&gt;limit($iLimit);<br />
$oPager-&gt;items($iItemsCount);<br />
$oPager-&gt;page($iPage);</code></p>
<p>Metody limit, items oraz page zwracają liczby odpowiadające ich nazwą niezależnie od tego, czy została podana nowa wartość w argumencie, czy nie, co jest absolutnie wygodnym (dla mnie) rozwiązaniem.</p>
<p>Aby wyświetlić oczekiwane rekordy, wykorzystujemy pagera:</p>
<p><code>$aData = $oModel-&gt;GetList($iUser, $oPager-&gt;start(), $oPager-&gt;limit());</code></p>
<p>Lub bezpośrednio w zapytaniu do bazy dancyh:</p>
<p><code>$sSql = "SELECT news_id FROM news LIMIT " . $oPager-&gt;start() . ", " . $oPager-&gt;limit();</code></p>
<p>Od strony widoku, prezentacja pagincaji prezentuje się w bardzo prosty sposób:</p>
<p>&lt;?php echo $oPager-&gt;Render(true); ?&gt;</p>
<p>Metoda render przyjmuje kolejno:</p>
<ol>
<li><em>$mMode</em> (mixed: bool, null, string), jeżeli true, paginacja jest zwracana w formacie HTML. Jeżeli null, numerki stron podane w formie array&#8217;a zamiast linków HTML. Jeżeli string, zostaje zwracana wartość po renderowaniu pagera, np. element startujący przedział mnożony po limicie: $oPager-&gt;Render(&#8216;start&#8217;);. Ten sam efekt powoduje metoda $oPager-&gt;start();</li>
<li><em>$iDrawCutLimit</em> (int) ilość numerków na początku i końcu łańcucha paginacji.</li>
<li><em>$iDrawCutLimitCenter</em> (int) ilość numerków &#8220;w środku&#8221; obok aktualnie przeglądanej strony.</li>
<li><em>$bNavigation</em> (bool) linki &#8220;poprzednia&#8221; i &#8220;następna&#8221; strona znajdujące się na początku i końcu łańcucha.</li>
</ol>
<p>Możemy sami ostylować linki generowane przez pager. Wystarczy że w widoku dodamy swój apperance:</p>
<p><code>$this-&gt;oPager-&gt;PatternPage('&lt;a href="?[$]"&gt;[$]&lt;/a&gt;');<br />
$this-&gt;oPager-&gt;PatternPageCurrent('&lt;strong&gt;[$]&lt;/strong&gt;');<br />
$this-&gt;oPager-&gt;PatternPageNavigation('&lt;a href="?[$]" rel="nofollow"&gt;[$$]&lt;/a&gt;', array('&amp;laquo; poprzednia', 'następna &amp;raquo;'));<br />
$this-&gt;oPager-&gt;PatternSeparator('&lt;span&gt;...&lt;/span&gt;');</code></p>
<p>Końcowy efekt, możemy nie wyświetlać pagera, gdy jest tylko jedna strona elementów:</p>
<p><code>&lt;?php if($this-&gt;oPager-&gt;Render('pages') &gt; 1) {<br />
// wyswietl pager...<br />
} ?&gt;</code></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/paginacja-php-stronicowanie/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Logowanie po nickname i email (usability)</title>
		<link>http://athlan.pl/logowanie-nickname-email-usability/</link>
		<comments>http://athlan.pl/logowanie-nickname-email-usability/#comments</comments>
		<pubDate>Sun, 28 Dec 2008 14:35:13 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Usablity]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[logowanie]]></category>
		<category><![CDATA[usability]]></category>
		<category><![CDATA[username]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=169</guid>
		<description><![CDATA[Projektując serwisy zaczynam ostrożnie podchodzić do usability. Temat jest bardzo wrażliwy, każdy webmaster to inne zdanie. Dziś chciałbym przedstawić problem logowania do serwisu. Oprócz hasła, przy logowaniu używamy: standardowo nazwy użytkownika, w niektórych serwisach adresu email, rzadziej spotykane zjawisko. W trosce o użytkowników w kilku moich serwisach zastosowałem możliwość logowania się na adres email lub [...]]]></description>
			<content:encoded><![CDATA[<p>Projektując serwisy zaczynam ostrożnie podchodzić do usability. Temat jest bardzo wrażliwy, każdy webmaster to inne zdanie. Dziś chciałbym przedstawić problem logowania do serwisu. Oprócz hasła, przy logowaniu używamy:</p>
<ul>
<li>standardowo <strong>nazwy użytkownika</strong>,</li>
<li>w niektórych serwisach <strong>adresu email</strong>, rzadziej spotykane zjawisko.</li>
</ul>
<p>W trosce o użytkowników w kilku moich serwisach zastosowałem możliwość logowania się na adres email lub nazwę użytkownika. Wszystko po to, aby ułatwić dostęp do ukrytej części witryny, aby nikt nie &#8220;zwątpił&#8221; bo nazwy użytkownika, lub adresu email, który podał przy zakładaniu profilu. Sam używam w sieci kilku adresów email oraz kilku prefixów i suffixów do nicka <strong>Athlan</strong>. Mam problem z zalogowaniem, gdy danie nie pasują, a potrzebuję dostęp tylko na chwilę.</p>
<p>Przygotujmy zatem tok myślenia programu, który pobierze dane, w zależności od tego, jakie dane podał użytkownik. Pomijam walidację hasła etc:</p>
<ol>
<li>Wykrycie, czy nick jest adresem email.</li>
<li>Jeżeli tak, pobierz dane użytkownika po polu <em>user_mail</em> i zapisz je do zmiennej <code>$aUser</code>.</li>
<li>Jeżeli nie, pobierz dane identyfikując rekord po kluczu <em>user_name</em> i zapisz pobrane dane do zmiennej <code>$aUser</code>.</li>
</ol>
<p>Jak możemy zauważyć, w obu przypadkach dane zapisujemy do tej samej zmiennej <code>$aUser</code>, więc możemy je dalej tak samo wykorzystywać. Różni się tylko pobieranie, dlateg nie trzeba w żadnym wypadku powielać kodu.</p>
<ul>
<li><a href="http://athlan.pl/code/UsablityLogin">Przykładowy kod logowania</a>.</li>
</ul>
<p>Jak słusznie zauważył <a href="http://greensky.pl">devnull</a>, <span style="text-decoration: underline;">należy wykluczyć możliwość użycia znaku małpy w loginie przy rejestracji użytkownika</span>. Wyjaśnienie znajdziecie w komentarzach.</p>
<p>Dla programisty nie jest wiele pracy, a warto ułatwić dostęp użytkownikowi do serwisu. <strong>Usability</strong> na pierwszym miejscu ;-)</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/logowanie-nickname-email-usability/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Podtrzymanie sesji</title>
		<link>http://athlan.pl/podtrzymanie-sesji/</link>
		<comments>http://athlan.pl/podtrzymanie-sesji/#comments</comments>
		<pubDate>Fri, 26 Dec 2008 19:35:39 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[Usablity]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[sesje]]></category>
		<category><![CDATA[sessions]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=159</guid>
		<description><![CDATA[Dla niektórych wygasanie sesji jest zabezpieczeniem (banki, etc.). Realizując jeden z projektów oczekiwałem od systemu tego, aby użytkownik nigdy nie gubił sesji, gdy ma otwarte okno w przeglądarce. Dlaczego? Może dodaje posta, być może uzupełnia dość obszerny tekst na stronie. Gdy klika zapisz, przerzuca go do strony logowania, a cały tekst zniknął za sprawą tego, [...]]]></description>
			<content:encoded><![CDATA[<p>Dla niektórych wygasanie sesji jest zabezpieczeniem (banki, etc.). Realizując jeden z projektów oczekiwałem od systemu tego, aby użytkownik nigdy nie gubił sesji, gdy ma otwarte okno w przeglądarce. Dlaczego? Może dodaje posta, być może uzupełnia dość obszerny tekst na stronie. Gdy klika <em>zapisz</em>, przerzuca go do strony logowania, a cały tekst zniknął za sprawą tego, że jego przeglądarka nie zapisuje wartości pól formularza. Skąd to znamy.</p>
<p><strong>Jak użytkownik gubi sesję?</strong></p>
<ol>
<li>Jego ciastko wygasa, więc serwer nie może go zidentyfikować z sesją.</li>
<li>Po jakimś czasie, choćby odtworzył ciastko, plik sesji znika z naszego serwera (<a href="http://pl.php.net/manual/pl/session.configuration.php#ini.session.gc-divisor">garbage collection</a>).</li>
</ol>
<p><strong>Rozwiązania:</strong></p>
<ol>
<li>Wydłużenie czasu wygasania ciastka i sesji.</li>
<li>Odświeżenie strony w interwale mniejszym, niż wynosi czas wygasania sesji i ciastka.</li>
</ol>
<p>Rozmyślając nad podtrzymaniem sesji, próbowałem znaleźć wszystkie metody oraz wybrać najlepszą. Wszystkie sprowadzają się do &#8220;odświeżenia&#8221; strony lub jej fragmentu tak, aby nasz silnik wykonał tylko potrzebne session_start(); czyli podtrzymanie aktywności sesji. Jest kilka mniej lub bardziej zadowalających sposobów:</p>
<ol>
<li><strong>Odświeżenie całej strony.</strong><br />
To może spowodować, że dane wprowadzane przez użytkownika w formularzu zostaną utracone. Ponad to, jeżeli użytkownik czyta newsy, denerwującym może być fakt, że lista nagle zostanie przescrollowana do góry (prócz opery).</li>
<li><strong>Wysłanie requestu ajax w tle.</strong><br />
Minusem jest to, że trzeba używać biblioteki ajax lub pisać dodatkowy kod javascriptu. Jeżeli ktoś na stronie używa jakiegoś ajaxa &#8211; co za różnica. Poza tym same plusy.</li>
<li><strong>Odświeżanie ukrytej ramki</strong> iframe lub elementu frameset.<br />
Minusów usablity prawie brak. Brak potrzeby instalacji javascriptów i ajaxa. Odświeżacz powinien wysłać nagłówek <em>Refresh</em> lub odpowiedni metatag.</li>
</ol>
<p>Sposób 3 wydaje mi się najlepszy. Można go ulepszyć w ten sposób, aby ramka nie wysyłała żądania zaraz po załadowaniu strony. Powodowałoby to podwójne requesty do serwera.</p>
<ul>
<li><a href="http://athlan.pl/code/PingIframe">Przykład z ukrytym iframe</a>.</li>
<li><a href="http://athlan.pl/code/PingAdvajax">Przykład z Advajax</a>.</li>
<li>plik <code>ping.php</code> wygląda wówczas następująco: <span style="color: #999999; font-size: 9px;">aplikacje nie używające frameworków ingerujących w standardowe działanie sesji</span><br />
<code>session_start(); header('Refresh: 60');</code></li>
</ul>
<p><span style="font-size: 9px">Zapewne znajdą się osoby, które powiedzą: a co z użytkownikami, którzy mają wyłączone ramki, lub ich przeglądarki w ogóle ich nie obsługują. Zapytam wówczas: a co z użytkownikami, którzy nie akceptują cisteczek (wówczas sesje nie są dla nich użyteczne, chyba, że użyjemy przesłyki jej identyfikatora w adresie url). Dopytam również: a co z użytkownikami, którzy mają wyłączony Javascript? Patologiczne przepadki się po prostu pomija ;)</span></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/podtrzymanie-sesji/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Hashowanie haseł z solą</title>
		<link>http://athlan.pl/hashowanie-hasel-z-sola/</link>
		<comments>http://athlan.pl/hashowanie-hasel-z-sola/#comments</comments>
		<pubDate>Mon, 22 Dec 2008 18:50:26 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[sha1]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=150</guid>
		<description><![CDATA[Przeglądając forum.php.pl często widziałem, jak użytkownicy przechowują hasła w swoich bazach danych. Najczęściej używają funkcji hashujących md5, sha1 i sha2. Wszystko wygląda bardzo dobrze, hasła są przechowywanie bezpiecznie. No właśnie&#8230; na ile bezpiecznie. Nie będę tutaj rozwodził się nad zabezpieczeniem baz danych, w których owa baza haseł się znajduje, ale nad samym zahashowanym ciągu. Wszyscy [...]]]></description>
			<content:encoded><![CDATA[<p>Przeglądając forum.php.pl często widziałem, jak użytkownicy przechowują hasła w swoich bazach danych. Najczęściej używają funkcji hashujących <code>md5</code>, <code>sha1</code> i <code>sha2</code>. Wszystko wygląda bardzo dobrze, hasła są przechowywanie bezpiecznie. No właśnie&#8230; na ile bezpiecznie.</p>
<p>Nie będę tutaj rozwodził się nad zabezpieczeniem baz danych, w których owa baza haseł się znajduje, ale nad samym zahashowanym ciągu. Wszyscy doskonale wiemy, że istnieją bazy md5 (sha1, sha2 również).</p>
<p><em>Przezorny zawsze ubezpieczony.</em> Wiadomo, że nigdy nic nie wiadomo.</p>
<p>Pokażę, jak dodatkowo zabezpieczyć nasze hasła. Będą przechowywane w tej samej bazie danych, używając tych samych metod hashowania, a jednak szansa na &#8220;złamanie&#8221; hasła (wyszukania w bazie) będzie niemożliwa. Posłużymy się ciągiem znaków zwanym przez programistów <em>solą </em>(<em>salt</em>). Przykład implementacji możemy znaleźć w forum IPB, natomiast phpBB pozbawione jest tego <span style="text-decoration: line-through;">fjuczuru</span> ficzera. Cała sprawa sprowadza się do wygenerowania dowolnego kawałka ciągu znaków i doklejenia go do hasła. Sól potrzebna nam będzie również przy porównaniu hasła, więc trzeba ją zapisać w bazie danych obok hasła.</p>
<p>Poniżej zamieszczam przykładową klasę, która obsługuje solenie haseł. Doklejanie soli może być napisane w dowolny sposób, zależy to od Waszej wyobraźni. Ja dodatkowo dodałem element &#8220;losowy&#8221; w postaci doklejenia do soli wyniku działania funkcji microtime().</p>
<ul>
<li><a href="http://athlan.pl/code/PassSalt">Klasa, która soli hasła.</a></li>
<li><a href="http://athlan.pl/code/PassSaltExample">Przykład</a> &#8211; zapisywanie hasła do bazy danych oraz sprawdzanie go przy logowaniu.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/hashowanie-hasel-z-sola/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Automatyczne przekierowanie po zalogowaniu</title>
		<link>http://athlan.pl/automatyczne-przekierowanie-po-zalogowaniu/</link>
		<comments>http://athlan.pl/automatyczne-przekierowanie-po-zalogowaniu/#comments</comments>
		<pubDate>Thu, 02 Oct 2008 21:21:31 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Przemyślenia]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=141</guid>
		<description><![CDATA[Denerwujące jest, że na niektórych portalach: nie ma automatycznego przekierowania do strony wymagającej autoryzacji po zalogowaniu się, w adresie strony jest parametr typu back_url=/bardzo/glugi/parametr/ze/czasem-to/wogole/szok.html Ostatnio programując sklep internetowy zastosowałem najprostszą technikę, która uwzględni oba powyższe punkty. W moim przypadku, gdy kontroler wymaga zalogowania się, warstwa ACL wskazuje na kontroler logowania (w zależności od konfiguracji). Natomiast [...]]]></description>
			<content:encoded><![CDATA[<p>Denerwujące jest, że na niektórych portalach:</p>
<ul>
<li>nie ma automatycznego przekierowania do strony wymagającej autoryzacji po zalogowaniu się,</li>
<li>w adresie strony jest parametr typu back_url=/bardzo/glugi/parametr/ze/czasem-to/wogole/szok.html</li>
</ul>
<p>Ostatnio programując sklep internetowy zastosowałem najprostszą technikę, która uwzględni oba powyższe punkty. W moim przypadku, gdy kontroler wymaga zalogowania się, warstwa ACL wskazuje na kontroler logowania (w zależności od konfiguracji). Natomiast URL zostaje, tj:</p>
<p>http://example.com/konto/dodaj-produkt.html</p>
<p>Pierwsze co trzeba zrobić, to sprawdzić, gdzie jest użytkownik. Jeżeli wywołujemy kontroler logowania pod powyższym adresem, trzeba porównać aktualny url do adresu logowania. W moim przypadku adresem logwania jest:</p>
<p>http://example.com/zaloguj.html</p>
<p>Aktualną pozycję użytkownika możemy sprawdizć w zmiennej $_SERVER['REQUEST_URI'], a adres logowania mamy z góry ustalony w naszej aplikacji. U mnie za adresu odpowiada router, stąd poniższy przykład przekierowania do strony logowania:</p>
<p><code>if(($sReq = $_SERVER['REQUEST_URI']) != ($sUrl = Vframe_Router::Route('auth_login')))<br />
{<br />
$this-&gt;_oUser-&gt;login_backlink = $sReq;<br />
return $this-&gt;_redirect($sUrl);<br />
}</code></p>
<p><em>UWAGA! Jeżeli przekierowujemy użytkownika do strony logowania, zamiast <code>$_SERVER['REQUEST_URI']</code> sprawdzamy <code>$_SERVER['HTTP_REFERER']</code>.</em></p>
<p>Metoda _redirect to nic innego, jak wysłanie <a href="http://www.google.pl/search?hl=pl&amp;q=location+header">header Location</a>. Dodatkowo do sesji użytkownika zapisuję adres, z którego użytkownik został przekierowany do strony logowania. Bardzo ważne jest to, żeby zapisać lokalizację tylko wtedy, gdy adres nie wskazuje na URL logowania, bo użytkownik po zalogowaniu zamiast powrócić do przegladanej strony, wróci do podstrony logowania.</p>
<p>Tuż po autoryzacji ustawiamy w sesji <code>login_backlink</code> na null, aby w przyszłości, gdy użytkownik wejdzie bezpośrednio na podstronę logowania, nie został odesłany do zapamiętanej wcześniej lokalizacji.</p>
<ul>
<li><a href="http://athlan.pl/code/LoginController">Przykładowy kod kontrolera</a> w mojej aplikacji.</li>
<li><a href="http://athlan.pl/code/LoginControllerSimple">Prostszy kod</a> dla ludzi nie używających frameworków.</li>
</ul>
<p>Mała modyfikacja aplikacji i nie denerwujemy użytkownika.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/automatyczne-przekierowanie-po-zalogowaniu/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Google Analytics for WordPress</title>
		<link>http://athlan.pl/google-analytics-for-wordpress/</link>
		<comments>http://athlan.pl/google-analytics-for-wordpress/#comments</comments>
		<pubDate>Wed, 13 Aug 2008 14:25:56 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Google]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=130</guid>
		<description><![CDATA[Od niedawna na moim blogu można dostrzec nową zakładkę: GoogleAnalytics plugin. Wtyczkę stworzyłem dla siebie, ma ona na celu dodanie kodu Javascript generowanego przez Google Analytics do kodu HTML, bezpośrednio przed tagiem &#60;/body&#62;. Po aktywacji pluginu w dziale ustawień pojawi się nowa podstrona umożliwiająca wpisanie ID statystyk. I wszystko sprowadza się do prostoty. Szczerze powiedziawszy [...]]]></description>
			<content:encoded><![CDATA[<p>Od niedawna na moim blogu można dostrzec nową zakładkę: <strong><a href="http://athlan.pl/googleanalytics/">GoogleAnalytics plugin</a></strong>.</p>
<p>Wtyczkę stworzyłem dla siebie, ma ona na celu dodanie kodu Javascript generowanego przez Google Analytics do kodu HTML, bezpośrednio przed tagiem &lt;/body&gt;. Po aktywacji pluginu w dziale ustawień pojawi się nowa podstrona umożliwiająca wpisanie ID statystyk. I wszystko sprowadza się do prostoty.</p>
<p>Szczerze powiedziawszy wtyczkę napisałem z lenistwa (paradoks) &#8211; nie chciało mi się szukać gotowego rozwiązania (których istnieje zapewne milion), a przy zmianie szablonu umknął mi fakt, że kod JS został wklejony &#8220;na sztywno&#8221; do pliku szablonu footer.php. Gdy chciałem sprawdzić statystyki, wyświetlił mi się pusty wykres.</p>
<p>Napisanie jej zajęło mi 10 minut (sic! z zegarkiem w ręku) i dużą satysfakcję z wykorzystania API wordpressa. <a href="http://athlan.pl/googleanalytics/">GoogleAnalytics plugin</a> został dodany do oficjalnego katalogu pluginów oraz otrzymał repozytorium SVN (które jest puste &#8211; wtyczka nie będzie dalej rozwiaja, bo ma być taka prosta, jak jest teraz).<strong><br />
</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/google-analytics-for-wordpress/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>WordPress sitemap plugin</title>
		<link>http://athlan.pl/wordpress-sitemap-plugin/</link>
		<comments>http://athlan.pl/wordpress-sitemap-plugin/#comments</comments>
		<pubDate>Tue, 28 Aug 2007 21:28:23 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://athlan.vgroup.pl/wordpress-sitemap-plugin/</guid>
		<description><![CDATA[Na potrzeby pewnego projektu o pewnej nazwie, o której niebawem się przekonacie, powstał nowy plugin. Generuje on sitemapa przyjaznego dla Google. Po co to wszystko&#8230; Rejestrując się w google mamy możliwość skorzystać z wielu narzędzi webmastera. Jednym z nich jest &#8220;pomaganie&#8221; robotowi w indeksowaniu naszej strony, poprzez podanie mu sitemapu po którym powinien się poruszać. [...]]]></description>
			<content:encoded><![CDATA[<p><img SRC="http://vgroup.pl/www/img/projects/project_contexlink_sitemap.jpg" ALIGN="left" BORDER="1" HEIGHT="160" HSPACE="10" VSPACE="10" WIDTH="160" />Na potrzeby pewnego projektu o pewnej nazwie, o której niebawem się przekonacie, powstał nowy plugin. Generuje on sitemapa przyjaznego dla <a HREF="http://google.com">Google</a>. Po co to wszystko&#8230; Rejestrując się w google mamy możliwość skorzystać z <a HREF="https://www.google.com/webmasters/tools/siteoverview">wielu narzędzi webmastera</a>. Jednym z nich jest &#8220;pomaganie&#8221; robotowi w indeksowaniu naszej strony, poprzez podanie mu sitemapu po którym powinien się poruszać. Po <a HREF="http://www.google.com/support/webmasters/bin/topic.py?topic=8472">zweryfikowaniu</a> naszej strony w systemie google, proszeni jesteśmy o podanie sitemapu (<a HREF="http://www.google.com/support/webmasters/bin/topic.py?topic=8472">więcej o weryfikacji</a>).</p>
<p ALIGN="left">W tym miejscu z pomocą przychodzi nam <strong>WordPress ContexlinkSitemap Plugin</strong>. Plugin przede wszystkim:</p>
<ul>
<li>tworzy mapę witryny uwzględniając wszystkie notki i podstrony na blogu oznaczone jako &#8220;published&#8221;,</li>
<li> uwzględnia strukturę permalinków, URL&#8217;i przyjaznych dla wyszukiwarek sprecyzowanych w konfiguracji bloga</li>
<li>datę ostatnich zmian na stronie głównej oraz w każdym linku z osobna</li>
<li>dostosowany do mapy google:<br />
<a HREF="https://www.google.com/webmasters/tools/docs/pl/protocol.html">https://www.google.com/webmasters/tools/docs/pl/protocol.html</a></li>
<li>jest bezpłatny : -)</li>
</ul>
<p ALIGN="left"><strong>Instalacja pluginu:</strong></p>
<ol>
<li><a HREF="http://athlan.vgroup.pl/wp-content/uploads/ContextlinkSitemap.rar">Pobierz paczkę pluginu stąd (format ZIP)</a></li>
<li>Folder <em>ContextlinkSitemap </em>skopiuj do folderu <em>wp-content/plugins/</em></li>
<li>Plik sitemap.xml skopiuj do folderu głównego bloga i nadaj mu chmod 777</li>
<li>Aktywuj plugin w zakładce <em>Plugins</em> wpanelu administracyjnym bloga</li>
</ol>
<p ALIGN="left"><strong>Dodanie mapy witryny do google:</strong></p>
<ol>
<li>Zaloguj się na google.com na swoje Google Account lub Gmail Account.</li>
<li>W nagłówku strony głównej google.com przejdź w sekcję &#8220;Moje konto&#8221;</li>
<li>Z listy &#8220;Moje usługi&#8221; wybierz pozycję &#8220;Narzędzia dla webmasterów&#8221;</li>
<li>Dodaj swoją stronę.</li>
<li>Wybierz formę weryfikacji strony i postępuj ze wskazówkami google</li>
<li>Gdy strona pojawi się w tabeli oraz jej status zostanie oznaczony jako &#8220;zweryfikowana&#8221;, kliknij w opcję &#8220;Dodaj mapę&#8221; i podaj adres URL mapy strony <em>(defaultowo http://twojblog.pl/sitemap.xml, czyli tam, gdzie wrzuciłeś plik)</em>.</li>
</ol>
<p ALIGN="left"><strong>Dodatkowa konfiguracja:</strong></p>
<p ALIGN="left">W celu szczegółowej konfiguracji pluginu, możesz:</p>
<ul>
<li>zmienić ścieżkę mapy w stałej CONTEXLINK_MAP_FILE</li>
<li>zmienić częstotliwość automatycznej aktualizacji mapy w stałej CONTEXLINK_MAP_UPDATE, wartość podawana w sekundach, defaultowo co 2 dni: 3600 * 24 * 2.</li>
<li>zmienić miejsce wykonywania aktualizacji, zwykle jest to przy ładowaniu sekcji head na blogu. Aby dokonać zmian, należy zmienić miejsce akcji: add_action(&#8216;wp_head&#8217;, &#8216;ContexlinkSitemapAutogenerate&#8217;); Pełną listę miejsc, w których można wykonać operację możesz znaleźć tutaj: <a HREF="http://codex.wordpress.org/Plugin_API/Action_Reference">http://codex.wordpress.org/Plugin_API/Action_Reference</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/wordpress-sitemap-plugin/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>13 piątek, pechowo? Nie&#8230; koniec PHP4</title>
		<link>http://athlan.pl/13-piatek-pechowo-nie-koniec-php4/</link>
		<comments>http://athlan.pl/13-piatek-pechowo-nie-koniec-php4/#comments</comments>
		<pubDate>Fri, 13 Jul 2007 09:19:23 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Wykop]]></category>

		<guid isPermaLink="false">http://athlan.vgroup.pl/13-piatek-pechowo-nie-koniec-php4/</guid>
		<description><![CDATA[PHP 4 end of life announcement Takim nagłówkiem dnia 13 lipca (tj. piątek) 2007 roku na oficjalnej stronie PHP ujawnił się news dotyczący oficjalnego zamknięcia projektu PHP 4. Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 [...]]]></description>
			<content:encoded><![CDATA[<blockquote>
<h2>PHP 4 end of life announcement</h2>
<p>Takim nagłówkiem dnia 13 lipca (tj. piątek) 2007 roku na <a href="http://php.net">oficjalnej stronie PHP</a> ujawnił się <a href="http://www.php.net/index.php#2007-07-13-1">news dotyczący oficjalnego zamknięcia projektu PHP 4</a>.</p></blockquote>
<blockquote><p>   Today it is exactly three years ago since PHP 5 has been released. In   those three years it has seen many improvements over PHP 4. PHP 5 is   fast, stable &amp; production-ready and as PHP 6 is on the way, PHP 4   will be discontinued.</p>
<p>The PHP development team hereby announces that support for PHP 4 will   continue until the end of this year only. After 2007-12-31 there will be no   more releases of PHP 4.4. We will continue to make critical security fixes   available on a case-by-case basis until 2008-08-08.  Please use the rest of   this year to make your application suitable to run on PHP 5.</p>
<p>For documentation on migration for PHP 4 to PHP 5, we would like to point you   to our <a href="http://www.php.net/manual/en/migration5.php">migration guide</a>. There is   additional information available in the <a href="http://www.php.net/manual/en/migration51.php">PHP 5.0 to PHP 5.1</a> and <a href="http://www.php.net/manual/en/migration52.php">PHP 5.1 to PHP 5.2</a> migration guides as   well.</p></blockquote>
<p><a href="http://forum.php.pl/Koniec-PHP4-konkretne-daty-t72832.html">Tłumaczenie</a> by <a href="http://blog.hwao.pl/">Paweł &#8216;hwao&#8217; Halicki</a>:</p>
<blockquote><p>Dziś (tj. 13 lipca 2007) mijają dokładnie trzy lata od daty wydania w pełni stabilnego PHP5. Przez ten okres czasu ciężko pracowaliśmy nad PHP4 czego owocem były liczne poprawki. Lecz teraz, kiedy PHP5 stało się szybkie, stabilne i w pełni dojrzałe, a prace nad PHP6 są w toku, możemy oznajmić iż projekt PHP4 zostanie zamknięty.</p>
<p>Deweloperzy pracujący nad PHP oświadczają, że wsparcie dla PHP4 będzie kontynuowane jedynie <strong>do końca tego roku</strong>. Po <strong>31 grudnia 2007</strong> nie będą ukazywać się już kolejne wersje PHP z linii 4.4. Wszystkie krytyczne błędy będą poprawiane będą jedynie do <strong>8 sierpnia 2008 roku</strong>. Prosimy aby wszyscy programiści, wykorzystali czas pozostały do końca roku, aby ich aplikacje były kompatybilne z PHP5.</p>
<p>Wszelkie niezbędne wskazówki pomocne w migracji z PHP4 do PHP5 możecie znaleźć w pod linkiem <a href="http://www.php.net/manual/en/migration5.php" target="_blank">migration guide</a>. Są też dostępne wskazówki jak migrować z <a href="http://www.php.net/manual/en/migration51.php" target="_blank">PHP5.0 do PHP5.1</a> a także <a href="http://www.php.net/manual/en/migration52.php" target="_blank">PHP5.1 do PHP5.2</a>.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/13-piatek-pechowo-nie-koniec-php4/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Autoload &#8211; praktyczne mapowanie</title>
		<link>http://athlan.pl/autoload/</link>
		<comments>http://athlan.pl/autoload/#comments</comments>
		<pubDate>Sat, 31 Mar 2007 20:21:58 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Framework]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Solutions]]></category>

		<guid isPermaLink="false">http://athlan.vgroup.pl/autoload/</guid>
		<description><![CDATA[O autoloadzie klas było już głośno, rozpatrywaliśmy wszelkie za i przeciw w wielu miejscach choćby tu, tu i tu. Mechanizm automatycznego ładowania potrzebnych nam klas jest bardzo dobrym sposobem na zrelaksowanie się poprzez brak przymusu ręcznego ładowania plików potrzebnych do działania aplikacji. Znajdźmy receptę na owe pytanie przedstawiając dwa najpopularniejsze sposoby trzymania klas w np. [...]]]></description>
			<content:encoded><![CDATA[<p style="margin-bottom: 0cm">O <a href="http://php.net/autoload">autoloadzie</a> klas było już głośno, rozpatrywaliśmy wszelkie za i przeciw w wielu miejscach choćby <a href="http://forum.php.pl/index.php?showtopic=40450">tu</a>, <a href="http://strzalek.net/blog/8/autoload-automatyczne-ladowanie-klas">tu</a> i <a href="http://forum.php.pl/index.php?showtopic=26107">tu</a>.</p>
<p style="margin-bottom: 0cm">Mechanizm automatycznego ładowania potrzebnych nam <a href="http://php.net/class">klas</a> jest bardzo dobrym sposobem na zrelaksowanie się poprzez brak przymusu ręcznego <a href="http://php.net/include">ładowania plików</a> potrzebnych do działania aplikacji. Znajdźmy receptę na owe pytanie przedstawiając dwa najpopularniejsze sposoby trzymania klas w np. <a href="http://pl.wikipedia.org/wiki/Framework">frameworkach</a>.</p>
<ul>
<li>
<p style="margin-bottom: 0cm"><a href="http://framework.zend.com/">Zend Framework</a> i <a href="http://rapide.pl">Rapide</a> mają dość sztywną, lecz nie wymagającą specjalnego kombinowania technikę umieszczania plików w core. Sposób nazewnictwa jest bardzo prosty: <em>Folder_Subfolder_Klasa</em> – owy  zapis odnosi się do 	<em>./Folder/Subfolder/Klasa.Class.php</em>.</p>
</li>
<li>
<p style="margin-bottom: 0cm">W <a href="http://framework.vgroup.pl">moim frameworku Vframe</a> 	zastosowałem technikę, w której pliki leżą sobie „wolność w core, nazewnictwo klas nie tyczy się ich ścieżki, np. <em>NazwaKlasy</em> może odnieść się do <em>./NazwaKlasy.Class.php</em> lub <em>./Folder/Subfolder/ NazwaKlasy.Class.php</em>.</p>
</li>
</ul>
<p style="margin-bottom: 0cm">Spróbuję opisać drugi – mój – sposób na ładowanie plików z core aplikacji. Potrzebna nam będzie klasa, która jest w stanie przeskanować całe core i zapisać je do tablicy. Aby było łatwiej, skan zostanie zaserializowany do pliku jako mapa folderu. Co jeżeli plik nie będzie istniał w core? Wówczas mapa zostanie stworzona od nowa, jeżeli to nie pomoże &#8211; wyrzucimy wyjątek.</p>
<p style="margin-bottom: 0cm">Projektujemy naszą klasę, nazwiemy ją przykładowo Autoload, metody:</p>
<ul>
<li>
<p style="margin-bottom: 0cm"><em><strong>Load</strong>($sLibrary, $bRemapped = false)</em> – metoda która będzie odpowiedzialna za załadowanie 	pliku oraz (jeżeli nie będzie istniała) stworzenie tablicy plików. W wypadku braku pliku zostanie ponownione wywołanie metody tworzenia mapy plików z przymusowym przeskanowaniem folderu klas oraz tym samym odpalenie metody samej siebie z przyjęciem drugiego jej parametru na true – brak ponownego skanowania folderu i wyrzut wyjątku.</p>
</li>
<li>
<p style="margin-bottom: 0cm"><em><strong>Map</strong>($bUseTemp = true) </em>– metoda 	odpowiedzialna za stworzenie tablicy z plikami na podstawie pliku mapy poprzez odserializowanie danych w nim zapisanych. Jeżeli parametr otrzyma wartość false lub plik mapy nie będzie możliwy do odczytu, wówczas tworzona zostanie mapa, zapisana do pliku 	mapy oraz do tablicy klasy.</p>
</li>
<li>
<p style="margin-bottom: 0cm"><em><strong>FormatName</strong>($sLibrary) </em>– metoda 	formatująca nazwę klasy: Klasa.Class.php</p>
</li>
</ul>
<p style="margin-bottom: 0cm">Wykorzystamy trzy stałe:</p>
<ul>
<li>
<p style="margin-bottom: 0cm"><em>const _<strong>LibDir</strong></em><strong> </strong>– określa ścieżkę folderu z klasami</p>
</li>
<li>
<p style="margin-bottom: 0cm"><em>const _<strong>LibSurfix</strong></em><strong> </strong>– surfixy plików (.Class.php)</p>
</li>
<li>
<p style="margin-bottom: 0cm"><em>const _<strong>LibMap</strong></em><strong> </strong>– ścieżka i nazwa pliku mapy</p>
</li>
</ul>
<p style="margin-bottom: 0cm">Potrzebna będzie nam jeden statyczny, prywatny parametr klasy, który jest sam w sobie jej singletonem, więc każda instancja klasy będzie z niego korzystała:</p>
<p style="margin-bottom: 0cm"><em>private static $_aCoreMap = array();</em></p>
<p>Ok, teraz wystarczy użyć funkcji <em>__autoload() </em>i wykorzystać naszą klasę:</p>
<p>[php]function __autoload($sLibrary)<br />
{<br />
try<br />
{<br />
Autoload::Load($sLibrary);<br />
}<br />
catch(AutoloadException $oException)<br />
{<br />
// some message to display<br />
die(&#8216;Autoload failed: &#8216; . $oException-&gt;getMessage());<br />
}<br />
}[/php]</p>
<p>Tworzymy osobisty wyjątek dla autoloadera:</p>
<p>[php]class AutoloadException extends Exception<br />
{</p>
<p>}[/php]</p>
<p>oraz samą klasę:</p>
<p>[php] class Autoload<br />
{<br />
const _LibDir = &#8216;Core/&#8217;;<br />
const _LibSurfix = &#8216;.Class.php&#8217;;<br />
const _LibMap = &#8216;Map.tmp&#8217;;</p>
<p>private static $_aCoreMap = array();</p>
<p>public static function Load($sLibrary, $bRemapped = false)<br />
{<br />
if(!count(self::$_aCoreMap))<br />
self::Map();</p>
<p>$sLibraryFile = self::FormatName($sLibrary);</p>
<p>if(!isset(self::$_aCoreMap[$sLibraryFile]))<br />
{<br />
if($bRemapped)<br />
// if we remapped core and library dosen&#8217;t exists &#8211; throw an exception<br />
throw new AutoloadException(&#8216;Library &#8220;&#8216; . $sLibrary . &#8216;&#8221; has not been found in directory map!&#8217;);<br />
else<br />
{<br />
// ok, let&#8217;s go map core again without using map from file<br />
self::Map(false);<br />
// try load class from core directory again&#8230;<br />
self::Load($sLibrary, true);<br />
}<br />
}</p>
<p>require_once(self::$_aCoreMap[$sLibraryFile]);<br />
}</p>
<p>public static function Map($bUseTemp = true)<br />
{<br />
// if map is ok and we counld use it, get the map from file&#8230;<br />
if($bUseTemp &amp;&amp; is_readable(self::_LibMap))<br />
self::$_aCoreMap = unserialize(file_get_contents(self::_LibMap));<br />
// &#8230;unless we have to scan core and make map<br />
else<br />
{<br />
// clear array and remamp directory<br />
self::$_aCoreMap = array();</p>
<p>foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::_LibDir)) as $oFile)<br />
{<br />
$sFile = $oFile-&gt;getFilename();</p>
<p>if(isset(self::$_aCoreMap[$sFile]))<br />
throw new AutoloadException(&#8216;Core duplication &#8220;&#8216;.$sFile.&#8217;&#8221; found!&#8217;);<br />
else<br />
self::$_aCoreMap[$sFile] = $oFile-&gt;getPathname();<br />
}</p>
<p>// ok, save map to file<br />
if(!file_put_contents(self::_LibMap, serialize(self::$_aCoreMap)))<br />
throw new VframeException(&#8216;Cannot create core map!&#8217;);<br />
}<br />
}</p>
<p>private static function FormatName($sLibrary)<br />
{<br />
return $sLibrary . self::_LibSurfix;<br />
}<br />
}<br />
?&gt;[/php]</p>
<p><img src="http://www.speedyshare.com/gf/pic2.gif" align="left" height="60" width="40" />Całą paczkę klasy i zastosowań możecie <a href="http://www1.speedyshare.com/data/682616883/2918261/10302420/Autoload.rar">pobrać tutaj</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/autoload/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
