<?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; Solutions</title>
	<atom:link href="http://athlan.pl/kategoria/solutions/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>Sat, 17 Jul 2010 18:54:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>jQuery Animate i Easing</title>
		<link>http://athlan.pl/jquery-animate-i-easing/</link>
		<comments>http://athlan.pl/jquery-animate-i-easing/#comments</comments>
		<pubDate>Sat, 08 May 2010 08:17:41 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[JS/Ajax]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[XHTML]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[interface]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=457</guid>
		<description><![CDATA[Nie można kwestionować faktu, że jQuery.animate() jest jednym z najbardziej potężnych narzędzi jQuery. Służy on do animowania atrybutów CSS (czyli zmiany ich wartości w czasie od obecnego stanu A do definiowanego stanu B). Najprostszą implementacją jQuery Animate jest podanie zbioru atrybutów CSS, które mają ulec zmianie oraz czasu, w jakim ta zmiana ma nastąpić. Nie będę się [...]]]></description>
			<content:encoded><![CDATA[<p>Nie można kwestionować faktu, że <code><a href="http://api.jquery.com/animate/">jQuery.animate()</a></code> jest jednym z najbardziej potężnych narzędzi <strong>jQuery</strong>. Służy on do animowania <strong>atrybutów CSS</strong> (czyli zmiany ich wartości w czasie od obecnego stanu A do definiowanego stanu B). Najprostszą implementacją <strong>jQuery Animate</strong> jest podanie zbioru atrybutów CSS, które mają ulec zmianie oraz czasu, w jakim ta zmiana ma nastąpić. Nie będę się zagłębiał w najprostsze przykłady użycia, <a rel="nofollow" href="http://api.jquery.com/animate/">są one dostępne w oficjalnej dokumentacji</a> jQuery.</p>
<p>Należy pamiętać, że dzięki jQuery jesteśmy w stanie nie tylko płynnie zmieniać kolory, wielkość czcionki, obramowanie, ale także pozycje elementów, nadając stronie dynamicznego kształtu. Domyślnym sposobem animowania (<em>easing</em>) jest płynne przechodzenie. Istnieje natomiast sposób na zmianę adaptera animowania. Robert Penner &#8211; autor pluginu <a rel="nofollow" href="http://gsgd.co.uk/sandbox/jquery/easing/"><strong>jQuery Easing</strong></a> dostarczył nam niewiarygodnie efektowne i proste w implementacji narzędzie. Na oficjalnej stronie pluginu można znaleźć <a rel="nofollow" href="http://gsgd.co.uk/sandbox/jquery/easing/">wiele przykładów animacji</a>, które dostarcza nam dodatek. Efekty widoczne są zwłaszcza przy animowaniu pozycji i wymiarów obiektu, ale następują także w przypadku zmiany koloru &#8211; czyli są aplikowane do zmiany stanu każdego z atrybutów CSS.</p>
<p>Dziś postaram się pokazać efekty, jakie można uzyskać za pomocą <strong>jQuery Animate</strong> rozszerzonego o <strong>jQuery Interface</strong> oraz <strong>Easing</strong>.</p>
<ul>
<li><a href="http://athlan.pl/code/jQueryAnimateEasing/">Przykład różnego rodzaju easingu</a>.</li>
</ul>
<p>Pierwszym krokiem jest wygenerowanie własnej biblioteki <a href="http://jqueryui.com/download">jQuery Interface</a>. Dzięki generatorowi, jesteśmy w stanie ściągnąć tylko te części Interface, które są nam de facto potrzebne, zmniejszając jednocześnie ilość kodu. Klikamy &#8216;deselect all components&#8217;, a w sekcji <em>Effects</em> wybieramy efekty, których będziemy używać. Mnie w tej chwili interesuje <em>Bounce</em> i <em>Slide</em>. W paczce otrzymamy wersję deweloperską (z wcięciami) oraz <em>minified</em>, gotową do publikacji na serwerze.</p>
<p>Do wykorzystania efektu slideowania a&#8217;la iPhone (elastyczne odbicie od krawędzi ściany ekranu) sprowadza się drobny kawałek kodu, w którym istotnym jest parametr <em>easing</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#example'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">animate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span> left<span style="color: #339933;">:</span> <span style="color: #CC0000;">500</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span> duration<span style="color: #339933;">:</span> <span style="color: #CC0000;">1000</span><span style="color: #339933;">,</span> easing<span style="color: #339933;">:</span> <span style="color: #3366CC;">'easeOutElastic'</span> <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>Na pewno komuś się przyda.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/jquery-animate-i-easing/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 => 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>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;">=&gt;</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#41;</span>
  <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">add</span><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;">-&gt;</span><span style="color: #004000;">render</span><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>. <u>Domyślnie wagi tagów zawierają się pomiędzy 1, a 10</u>. 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;">-&gt;</span><span style="color: #004000;">render</span><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;">-&gt;</span><span style="color: #004000;">render</span><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;">=&gt;</span> <span style="color: #000088;">$aRow</span><span style="color: #009900;">&#41;</span>
  <span style="color: #000088;">$oCloud</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">add</span><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;">-&gt;</span><span style="color: #004000;">render</span><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;">=&gt;</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 href=&quot;'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">route</span><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; 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;&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>MySQL: remove duplicate entries/rows</title>
		<link>http://athlan.pl/mysql-remove-duplicate/</link>
		<comments>http://athlan.pl/mysql-remove-duplicate/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 08:02:04 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[delete]]></category>
		<category><![CDATA[duplicated]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[remove]]></category>
		<category><![CDATA[rows]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=332</guid>
		<description><![CDATA[Usuwając coś permanentnie z bazy danych musimy być bardzo ostrożni, bowiem przywrócenie danych jest bardzo trudne, czasem niemożliwe. Podstawową strukturę bazy danych powinno się budować na samym początku tworzenia aplikacji, z biegiem czasu rozbudowywać ją, ale unikać przebudowywania. Niestety są przypadki, gdzie trzeba przebudować jedną rzecz, co powoduje zmianę w wielu warstwach nie tyle aplikacji, [...]]]></description>
			<content:encoded><![CDATA[<p>Usuwając coś permanentnie z bazy danych musimy być bardzo ostrożni, bowiem przywrócenie danych jest bardzo trudne, czasem niemożliwe. Podstawową strukturę bazy danych powinno się budować na samym początku tworzenia aplikacji, z biegiem czasu rozbudowywać ją, ale unikać przebudowywania. Niestety są przypadki, gdzie trzeba przebudować jedną rzecz, co powoduje zmianę w wielu warstwach nie tyle aplikacji, co strukturze bazodanowej.</p>
<p>Dziś postaram się opisać, jakie kroki trzeba wykonać, aby bezpiecznie usunąć zdublowane rekordy z bazy danych nie tracąc żadnych danych:</p>
<ol>
<li>Tworzymy dwie <strong>kopie bazy danych</strong> lub tabel, na których będziemy pracowali. Najlepiej, aby pracować na drugiej kopii, nigdy na oryginale, a potem wdrożyć zmiany z drugiej kopii na oryginał. Przezorny zawsze ubezpieczony.</li>
<li>Analiza danych w tabeli. Musimy dokładnie wiedzieć jakie są relacje między tabelami, kiedy występują JOIN&#8217;y itp. Jeżeli rekordy są zdublowane, a posiadają ustalony ID, do których odwołuje się inny rekord z sąsiedniej tabeli, trzeba będzie w niej zmienić ID rekord zdublowanego na ID &#8220;substytuta&#8221;, bądź takiego rekordu, który nie spowoduje zmian w serwisie.</li>
<li>Wykonanie operacji <strong>usunięcia zdublowanych rekordów</strong>.</li>
</ol>
<p>Po wykonaniu kroku pierwszego zabieramy się za kolejny. Jest to najważniejszy moment naszych operacji. Aby ułatwić zrozumienie problemu, podam przykład z życia. Aplikacja posiadała poważny błąd, który umożliwiał zdublowanie użytkowników, ściślej: można było zdublować username. Za każdym razem, gdy użytkownik się logował i pisał komentarze, był ich właścicielem, ale comment_author posiadały różne ID tego samego użytkownika. Zaraz po skopiowaniu bazy danych spróbowałem przepisać ID autorów komentarzy na pierwszy rekord identyfikujący użytkownika, jaki istnieje w tabeli użytkowników. Skonstruowałem zapytanie:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> cms_comments
<span style="color: #993333; font-weight: bold;">JOIN</span> cms_members <span style="color: #993333; font-weight: bold;">AS</span> user_original <span style="color: #993333; font-weight: bold;">ON</span><span style="color: #66cc66;">&#40;</span>user_original<span style="color: #66cc66;">.</span>user_id <span style="color: #66cc66;">=</span> comment_author<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">SET</span> comment_author <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span>
  <span style="color: #993333; font-weight: bold;">SELECT</span> user_first<span style="color: #66cc66;">.</span>user_id <span style="color: #993333; font-weight: bold;">FROM</span> cms_members <span style="color: #993333; font-weight: bold;">AS</span> user_first
  <span style="color: #993333; font-weight: bold;">WHERE</span> user_first<span style="color: #66cc66;">.</span>user_name <span style="color: #66cc66;">=</span> user_original<span style="color: #66cc66;">.</span>user_name
  <span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> user_first<span style="color: #66cc66;">.</span>user_id <span style="color: #993333; font-weight: bold;">ASC</span> <span style="color: #993333; font-weight: bold;">LIMIT</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Usunięcie zdublowanych użytkowników było już tylko formalnością. Teraz się okaże, dlaczego zależało mi na wyciągnięciu dokładnie pierwszego rekordu reprezentującego &#8220;unikalnego&#8221; użytkownika: poniższe zapytanie (<strong>ALTER IGNORE TABLE ADD UNIQUE</strong>) usunie wszystkie kolejne rekordy oznaczone jako <strong>duplicated</strong>:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">ALTER</span> <span style="color: #993333; font-weight: bold;">IGNORE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> cms_members <span style="color: #993333; font-weight: bold;">ADD</span> <span style="color: #993333; font-weight: bold;">UNIQUE</span> <span style="color: #993333; font-weight: bold;">INDEX</span><span style="color: #66cc66;">&#40;</span>user_name<span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Krótki komentarz z manuala do <strong>ALTER TABLE</strong>:</p>
<blockquote><p><strong>IGNORE</strong> is a <span style="text-decoration: underline;">MySQL extension to standard SQL</span>. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, <span style="text-decoration: underline;">only the first row is used of rows with duplicates on a unique key</span>, The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.</p></blockquote>
<p>Pisząc ostatnie posty związane z bazami danych, mam nadzieję, że komuś się przydadzą.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-remove-duplicate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL: how to convert NULL to 0 number/int</title>
		<link>http://athlan.pl/mysq-convert-null-to-0/</link>
		<comments>http://athlan.pl/mysq-convert-null-to-0/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 23:24:57 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[zero]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=321</guid>
		<description><![CDATA[Im więcej nietypowych rzeczy programuję, tym więcej nietypowych problemów musze pokonać. Co powiecie na sumę 2 liczb, z których jedna jest wartością NULL powstałą w wyniku działania SUM() lub pochodnych, gdzie nie odnaleziono żadnego rekordu. Badamy: SELECT 1+2+3 &#62;&#62; 6 SELECT 1+2+NULL &#62;&#62; NULL SELECT COALESCE( NULL, 0 ) &#62;&#62; 0 Zatem analogicznie do powyższego [...]]]></description>
			<content:encoded><![CDATA[<p>Im więcej nietypowych rzeczy programuję, tym więcej nietypowych problemów musze pokonać. Co powiecie na sumę 2 liczb, z których jedna jest wartością <strong>NULL</strong> powstałą w wyniku działania <strong>SUM()</strong> lub pochodnych, gdzie nie odnaleziono żadnego rekordu.</p>
<p>Badamy:</p>
<p><code>SELECT 1+2+3<br />
&gt;&gt; 6</code></p>
<p><code>SELECT 1+2+NULL<br />
&gt;&gt; NULL</code></p>
<p><code>SELECT COALESCE( NULL, 0 )<br />
&gt;&gt; 0</code></p>
<p>Zatem analogicznie do powyższego przykładu:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> users <span style="color: #993333; font-weight: bold;">SET</span> user_points <span style="color: #66cc66;">=</span> user_points <span style="color: #66cc66;">+</span> COALESCE<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> SUM<span style="color: #66cc66;">&#40;</span> <span style="color: #66cc66;">...</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">...</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Punkty użytkownika już zawsze będą się sumowały poprawnie :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysq-convert-null-to-0/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>MySQL UPDATE JOIN</title>
		<link>http://athlan.pl/mysql-update-join/</link>
		<comments>http://athlan.pl/mysql-update-join/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 09:46:27 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[join]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[update]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=317</guid>
		<description><![CDATA[Ostatnimi czasy potrzebowałem danych z sąsiedniej tabeli przy UPDATE jedngo z pól w bazie danych. Danych do przetworzenia było sporo, więc zwracałem uwagę na wydajność zapytania. Aby zebrać potrzebne informacje, można użyć jednego ze sposobów: Zebrać potrzebne dane za pomocą SELECT&#8216;a, co sprawiłoby, że zajęta zostanie niepotrzebna pamięć w środowisku PHP podczas przypisania rezultatu do [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnimi czasy potrzebowałem danych z sąsiedniej tabeli przy <strong>UPDATE</strong> jedngo z pól w bazie danych. Danych do przetworzenia było sporo, więc zwracałem uwagę na wydajność zapytania. Aby zebrać potrzebne informacje, można użyć jednego ze sposobów:</p>
<ol>
<li>Zebrać potrzebne dane za pomocą <strong>SELECT</strong>&#8216;a, co sprawiłoby, że zajęta zostanie niepotrzebna pamięć w środowisku PHP podczas przypisania rezultatu do zmiennej.</li>
<li>Wykonać <strong>SET z podzapytaniem</strong>, ale potrzebnych mi było kilka kolumn z sąsiedniej tabeli, podzapytanie może zwrócić tylko jedną określoną wartość.</li>
<li>Wykonać <strong>JOIN</strong> przy update, czego niestety wówczas nie potrafiłem zrobić.</li>
</ol>
<p>Kartkując <a href="http://dev.mysql.com/doc/refman/5.0/en/update.html">manual</a> nie natrafiłem się w standardowej dokumentacji na nic konkretnego, aż nie spojrzałem na bardzo przydatne <a href="http://dev.mysql.com/doc/refman/5.0/en/update.html#c5402">komentarze użytkowników</a>. Okazało się, że przy <strong>UPDATE</strong> można wykonywać dowolne <strong>JOIN</strong>&#8216;y, schemat jest następujący:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> <span style="color: #993333; font-weight: bold;">JOIN</span> another_table <span style="color: #993333; font-weight: bold;">SET</span> <span style="color: #66cc66;">...</span></pre></div></div>

<p>W tym momencie mamy do dyspozycji wszystkie pola z dołączonej tabeli. Bardzo przydatne.</p>
<p><strong>Przykład z życia.</strong></p>
<p>Miałem za zadanie odznaczyć typy bukmacherskie na trafione, nietrafione, odwołane z przyczyn odwołania całego meczu piłkarskiego oraz te, które jeszcze nie mogą zostać oznaczone jako trafione lub nie, gdyż mecz się jeszcze nie odbył.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">UPDATE</span> typer_tickets_items
<span style="color: #993333; font-weight: bold;">LEFT</span> <span style="color: #993333; font-weight: bold;">JOIN</span> typer_events <span style="color: #993333; font-weight: bold;">ON</span><span style="color: #66cc66;">&#40;</span>event_id <span style="color: #66cc66;">=</span> item_event<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">SET</span> item_status <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">&#40;</span>
CASE
WHEN event_status <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span> THEN <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #808080; font-style: italic;"># mecz nie zostal rozegrany</span>
WHEN event_status <span style="color: #66cc66;">=</span> <span style="color: #66cc66;">-</span><span style="color: #cc66cc;">1</span> THEN <span style="color: #66cc66;">-</span><span style="color: #cc66cc;">1</span> <span style="color: #808080; font-style: italic;"># mecz anulowany</span>
WHEN event_status <span style="color: #66cc66;">=</span> item_bet THEN <span style="color: #cc66cc;">1</span> <span style="color: #808080; font-style: italic;"># typ trafiony</span>
ELSE <span style="color: #cc66cc;">0</span> END <span style="color: #808080; font-style: italic;"># typ nietrafiony</span>
<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">WHERE</span> item_status <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span></pre></div></div>

<p>Mam nadzieję, że komuś się przyda&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-update-join/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Przypadki w MySQL &#8211; CASE WHEN THEN ELSE END</title>
		<link>http://athlan.pl/mysql-case-when-then-else-end/</link>
		<comments>http://athlan.pl/mysql-case-when-then-else-end/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 13:15:07 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[Add new tag]]></category>
		<category><![CDATA[case]]></category>
		<category><![CDATA[conditions]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[syntax]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=299</guid>
		<description><![CDATA[Podobnie jak w PHP, baza danych MySQL ma odpowiednik if, czyli przypadków (inaczej serii warunków, instrukcji warunkowych). Różnicą między implementacją CASE&#8216;a w MySQL i ifa PHP jest to, że baza danych zwraca konkretną wartość z case&#8217;a, a nie wykonuje dowolnej ilości dowolnych akcji. CASE Syntax: Najprostsza struktura CASE&#8217;aprzedstawia się nastepująco: CASE WHEN [conditions] THEN ... ELSE [...]]]></description>
			<content:encoded><![CDATA[<p>Podobnie jak w PHP, baza danych MySQL ma odpowiednik <a href="http://pl2.php.net/manual/en/control-structures.if.php">if</a>, czyli przypadków (inaczej serii warunków, instrukcji warunkowych). Różnicą między implementacją <a href="http://dev.mysql.com/doc/refman/5.0/en/case-statement.html">CASE</a>&#8216;a w MySQL i ifa PHP jest to, że baza danych zwraca konkretną wartość z case&#8217;a, a nie wykonuje dowolnej ilości dowolnych akcji.</p>
<p><strong>CASE Syntax:</strong></p>
<p>Najprostsza struktura CASE&#8217;aprzedstawia się nastepująco:</p>
<p><code>CASE WHEN [conditions] THEN ... ELSE ... END</code></p>
<p>Składnia powinna rozpocząć się słowem kluczowym CASE, a zakończyć END. Pomiędzy znajdują się warunki WHEN oraz operacja zwrócenia odpowiedniej wartości, która po nich następuje THEN (mamy możliwość uwzględnić nieskończenie wiele warunków). Jeżeli żaden warunek nie zostanie spełniony możemy użyć opcjonalnie ELSE.</p>
<p><strong>Przykłady z życia.</strong></p>
<p>Wyobraźmy sobie, że mamy posortować listę aukcji przedmiotów na Allegro od najtańszych, do najdroższych. Należy założyć, że są 2 typy aukcji: kup teraz i licytacja. Pole licytacji w bazie danych zawiera największą zaproponowaną kwotę przez użytkowników w procesie licytacji, a cena kup teraz ustalana jest przez sprzedającego. Są to dwa różne pola w bazie danych, a jedno kryterium sortowania, dlatego trzeba scalić cenę w jedną, wybierając odpowiednią. Musimy przewidzieć sytuację, w której aukcja jest typu kup teraz oraz licytacji, wówczas jeżeli najwyższa oferta jest większa od ceny kup teraz, wówczas wybieramy pole z największa propozycją:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #66cc66;">&#40;</span>
  CASE
    WHEN <span style="color: #66cc66;">&#40;</span>auction_type <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'bidding'</span> <span style="color: #993333; font-weight: bold;">OR</span> auction_price_bid <span style="color: #66cc66;">&gt;</span> auction_price_buynow<span style="color: #66cc66;">&#41;</span>
      THEN auction_price_bid
    ELSE auction_price_buynow
  END<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> auction_price</pre></div></div>

<p>Stworzyliśmy pole auction_price, po którym można sortować aukcje od najtańszej do najdroższej i na odwrót.</p>
<p>Mam nadzieję, że krótki wpis przyda się początkującym. <span style="color: #c0c0c0;">Nic więcej nie trzeba opisywać, temat wydaje się co najmniej trywialny.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/mysql-case-when-then-else-end/feed/</wfw:commentRss>
		<slash:comments>3</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>5</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>8</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>17</slash:comments>
		</item>
		<item>
		<title>wordwrap() czy linewrap()</title>
		<link>http://athlan.pl/wordwrap/</link>
		<comments>http://athlan.pl/wordwrap/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 10:57:22 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Planeta]]></category>
		<category><![CDATA[Solutions]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=143</guid>
		<description><![CDATA[Rzadko bo rzadko, ale jestem zmuszony używać funkcji wordwrap(). Zawsze denerwowało mnie, że wordwrap łamie linię (ciągi znaków) a nie wyrazy (jak na to nazwa funkcji wskazuje). Czy aby na pewno nazwa funkcji jest trafna? Zwraca łańcuch str zawinięty w kolumny o odpowiedniej ilości znaków określonej przez opcjonalny parametr szerokość . Linia jest łamana przy [...]]]></description>
			<content:encoded><![CDATA[<p>Rzadko bo rzadko, ale jestem zmuszony używać funkcji <strong><a href="http://php.net/wordwrap">wordwrap</a></strong>(). Zawsze denerwowało mnie, że <strong>wordwrap</strong> łamie linię (ciągi znaków) a nie wyrazy (jak na to nazwa funkcji wskazuje). Czy aby na pewno nazwa funkcji jest trafna?</p>
<blockquote><p>Zwraca łańcuch <em><tt class="parameter">str</tt></em> zawinięty      w kolumny o odpowiedniej ilości znaków określonej przez opcjonalny parametr      <em><tt class="parameter">szerokość</tt></em> .  <strong><span style="text-decoration: underline;">Linia jest łamana</span></strong> przy użyciu (opcjonalnego)parametru <em><tt class="parameter">break</tt></em> .</p></blockquote>
<p>Niestety, potrzebowałem załamać wyrazy, a nie linie. Z pomocą przychodzi <strong><a href="http://php.net/wordwrap">preg_replace</a></strong>(). Założenie jest takie, że wyrazem jest każdy ciąg znaków nie zawierający spacji. Łamiemy nawet ciągi liczb oraz znaków specjalnych, niekoniecznie zawierających litery:</p>
<pre>function _wordwrap($sString, $iLimit = 32)
{
  return preg_replace('/([^ ]{' . (int)$iLimit . '})/', '$1 ', $sString);
}</pre>
<p>Paradoksy w nazewnictwie się zdarzają.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/wordwrap/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Weryfikacja właściciela strony</title>
		<link>http://athlan.pl/weryfikacja-wlasciciela-strony/</link>
		<comments>http://athlan.pl/weryfikacja-wlasciciela-strony/#comments</comments>
		<pubDate>Sun, 17 Aug 2008 09:48:58 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Framework]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Solutions]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=133</guid>
		<description><![CDATA[Niedługo być może zaimplementuje w jeden z moich nowych projektów weryfikację właściciela strony. Pomysł pozwoliłem zrobię zaczerpnąć z Google Webmaster Tool. Natomiast troszeczkę zmieniłem jeden ze sposobów, aby mniej się napracować. Zweryfikowanie właściciela witryny odbywa się po wywołaniu jednej z poniższych metod. Oto one: Weryfikacja poprzez upload pliku na serwer. Chwilowe dodanie meta-tagu do sekcji [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright" style="margin-left: 10px; margin-right: 10px; float: right;" src="http://img232.imageshack.us/img232/194/bannerwebsitedesignbx2.jpg" alt="" width="352" height="352" />Niedługo być może zaimplementuje w jeden z moich nowych projektów weryfikację właściciela strony. Pomysł pozwoliłem zrobię zaczerpnąć z Google Webmaster Tool. Natomiast troszeczkę zmieniłem jeden ze sposobów, aby mniej się napracować. Zweryfikowanie właściciela witryny odbywa się po wywołaniu jednej z poniższych metod. Oto one:</p>
<ul>
<li>Weryfikacja poprzez upload pliku na serwer.</li>
<li>Chwilowe dodanie meta-tagu do sekcji HEAD.</li>
</ul>
<p>W stosunku do google, zmianie uległa u mnie weryfikacja pierwsza.</p>
<p>Sposób Google: Upload pliku bez zawartości (lub z dowolną) o nazwie jako hash aktywacyjny, np: <code>&lt;?php md5( secret-salt ) . '.html'; ?&gt;</code></p>
<p>Mój sposób: Upload pliku ze stałą lub hashowaną nazwą o zawartości 32 znakowego hashu md5.</p>
<p>Zmienia się tylko zawartość pliku. Dlaczego? Niektóre strony zamiast zwracać kod 404 dla nieistniejących plików/podstron zwracają kod 200 (sukces). Wówczas trudno stwierdzić, czy jest to oczekiwany plik. Google dodatkowo sprawdza, czy strona zawsze zwraca kod 200, ale po co się babrać i wykonywać więcej requestów, jak można zrobić to trochę prościej. Jeżeli plik nie będzie fizycznie istniał na serwerze, a strona zwróci kod 200, skrypt musi oczekiwać w odpowiedzi tylko i wyłącznie hashu strony. Nazwa pliku na serwerze może być stała, ale niekoniecznie (dla większego bezpieczeństwa zalecane jest logiczne hashowanie nazwy pliku).</p>
<p>Przygotujmy zatem zarys klasy (od tego zawsze zaczynam):</p>
<ul>
<li><code>public function CheckFile($sUrl, $sFilename)</code> funkcja sprawdzająca hash w pliku na serwerze.</li>
<li><code>public function CheckMetatag($sUrl, $sTagName)</code> funkcja sprawdzająca hash w metatagu.</li>
<li><code>public static function Hash($sUrl)</code> funkcja budująca hash na postawie adresu URL (jako że podajemy go jako parametr obu metod sprawdzających, zmienna jest łatwo dostępna dla systemu).</li>
<li><code>public static function Metatag($sHash, $sTagName)</code> generowanie kodu XHTML dla metatagu &#8211; metoda potrzebna nam przy podaniu użytkownikowi meta tagu oraz do prega w metodzie CheckMetatag();</li>
<li><code>protected static function _Request($sUrl)</code> tworzenie requestu za pomocą HttpRequest i zwracanie treści metodą getResponseBody() wspomnianej klasy.</li>
</ul>
<p>Jako że komponent jest niejako zewnętrzną biblioteką, nie mogłem go wcisnąć w komponentu frameworka. Otrzymał status biblioteki: VframeLib_WebVeryfication.</p>
<p>Oczekiwane API:</p>
<p><code>var_dump(VframeLib_WebVeryfication::CheckFile('http://example.com/', 'Veryfication.txt')); // bool result<br />
var_dump(VframeLib_WebVeryfication::CheckMetatag('http://example.com/', 'Veryfication'));</code><code> // bool result</code></p>
<p>Gotowy kod:</p>
<p><a href="http://athlan.pl/code/VframeLib_WebVeryfication">http://athlan.pl/code/VframeLib_WebVeryfication</a></p>
<p><em style="color: red">Uwaga. Kod jest w fazie testowej. Wszelkie Wasze komentarze będą uwzględniane przy poprawkach.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/weryfikacja-wlasciciela-strony/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Ocenpizze.pl</title>
		<link>http://athlan.pl/ocenpizze/</link>
		<comments>http://athlan.pl/ocenpizze/#comments</comments>
		<pubDate>Thu, 24 Jul 2008 14:29:41 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[JS/Ajax]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Solutions]]></category>
		<category><![CDATA[Wykop]]></category>

		<guid isPermaLink="false">http://athlan.pl/?p=126</guid>
		<description><![CDATA[22 lipca oficjalnie wystartował mój nowy projekt: ocenpizze.pl. Każdy użytkownik może m.in.: oceniać pizzerie wg 3 kategorii: wystrój, obsługa oraz jakość podawanego jedzenia wraz z dodaniem osobistego komentarza, wyszukiwać pizzerie w swoim mieście/województwie, uzyskiwać informacje o aktualnie otwartych lokalach w danej lokalizacji w danym czasie (innowacyjny pomysł na polskiej scenie gastronomicznej), proponować nowe lokale. Właściciele [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright" style="border: 1px solid black; margin-left: 10px; margin-right: 10px; float: right;" src="http://img168.imageshack.us/img168/786/brooklynstylepizza712way8.jpg" alt="" width="300" height="150" />22 lipca oficjalnie wystartował mój nowy projekt: <a href="http://www.ocenpizze.pl">ocenpizze.pl</a>. Każdy użytkownik może m.in.:</p>
<ul>
<li><a href="http://www.ocenpizze.pl/">oceniać</a> pizzerie wg 3 kategorii: wystrój, obsługa oraz jakość podawanego jedzenia wraz z dodaniem osobistego komentarza,</li>
<li><a href="http://www.ocenpizze.pl/szukaj.html">wyszukiwać</a> pizzerie w swoim mieście/województwie,</li>
<li>uzyskiwać informacje o <a href="http://www.ocenpizze.pl/aktualnie-otwarte.html">aktualnie otwartych lokalach</a> w danej lokalizacji w danym czasie (innowacyjny pomysł na polskiej scenie gastronomicznej),</li>
<li><a href="http://www.ocenpizze.pl/panel/dodaj.html">proponować nowe lokale</a>.</li>
</ul>
<p>Właściciele lokali mogą:</p>
<ul>
<li>uzyskać dostęp do panelu administracyjnego,</li>
<li>uzupełniać godziny otwarcia swojego lokalu,</li>
<li>dodawać fotografie i logo lokalu,</li>
<li>tworzyć prezentację swojej firmy na zasadzie tworzenia zakładek.</li>
</ul>
<p>Serwis po dwudniowej pracy zyskał sympatię wielu użytkowników. Tych, którym podoba się serwis, zapraszam na <a href="http://www.wykop.pl/link/80447/ocen-swoja-pizzerie-szukaj-ktore-lokale-w-twoim-miescie-sa-otwarte">wykop</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/ocenpizze/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Obrona przed spamem</title>
		<link>http://athlan.pl/obrona-przed-spamem/</link>
		<comments>http://athlan.pl/obrona-przed-spamem/#comments</comments>
		<pubDate>Sat, 15 Mar 2008 19:10:03 +0000</pubDate>
		<dc:creator>Athlan</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Przemyślenia]]></category>
		<category><![CDATA[Publikacje]]></category>
		<category><![CDATA[Solutions]]></category>

		<guid isPermaLink="false">http://athlan.vgroup.pl/obrona-przed-spamem/</guid>
		<description><![CDATA[Kiedyś poruszyłem temat spamu w postaci niechcianych komentarzy, wpisów w księdze gości, whatever. Unikanie captchy jest oczywiste. Zbiorę teraz niektóre metody unikania spamu: Zbudowanie ukrytego pola zatytułowanego dla przeglądarek tekstowych za pomocą label: &#8220;tego pola nie wypełniaj&#8221;. Jeżeli jego wartość będzie różna od pustego stringu, oznacza to, że formularza na 100% nie wypełniał człowiek (pomijamy [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://img137.imageshack.us/img137/8600/spamfx4ji2.jpg" align="right" border="1" hspace="5" vspace="5" />Kiedyś <a href="http://athlan.vgroup.pl/jak-sie-bronic-przed-spamem/">poruszyłem temat spamu</a> w postaci niechcianych komentarzy, wpisów w księdze gości, whatever. Unikanie <a href="http://pl.wikipedia.org/wiki/CAPTCHA">captchy</a> jest oczywiste. Zbiorę teraz niektóre metody unikania spamu:</p>
<ul>
<li>Zbudowanie ukrytego pola zatytułowanego dla przeglądarek tekstowych za pomocą <em>label</em>: &#8220;tego pola nie wypełniaj&#8221;. Jeżeli jego wartość będzie różna od pustego stringu, oznacza to, że formularza na 100% nie wypełniał człowiek <em>(pomijamy patologiczne przypadki)</em>.</li>
<li>Zmiana nazw pól, np z <em>comment </em>na <em>athlan</em>, lub z <em>email </em>na <em>betband</em>. W ten sposób można przeprowadzać walidację adresu email, robot nie powinien się zorientować.</li>
<li>Zmiana wartości argumentu <em>action </em>dla <em>form</em>. W tym artykule poruszę nieco ten temat.</li>
</ul>
<p>Boty analizując kod HTML strony w formie XML&#8217;a wyłapuje podelementy (pola <em>input</em>) i ich argumenty (włącznie z argumentami <em>form</em>). Na podstawie tych danych stwierdza, jak wysłać request do serwera, aby dodać spam. Pomyślmy chwilkę&#8230; jeżeli argument wskazywałby na wadliwy adres, bot mógłby na niego wskazać. Programista ma do dyspozycji Javascript, który ma dostęp do <em>DOM</em> struktury kodu strony www.</p>
<p>Krytycy takich wycieczek powiedzą, że pewien procent użytkowników nie ma dostępu do javascript (przeglądarki tekstowe) lub ich ustawienia nie interpretują tego języka (opcja wyłączona). Alternatywą dla takiej sytuacji jest wskazanie w kodzie HTML dla atrybutu <em>action </em>adresu url, który wyświetli komunikat, aby włączyć javascript potrzebny do funkcjonowania serwisu.</p>
<p>Pozostałym doradzam, aby jako domyślny adres użyli <a href="http://example.com"><em>example.com</em></a>, <a href="http://example.net"><em>example.net</em></a> lub <a href="http://example.org"><em>example.org</em></a>. Klauzula 3 w dokumencie <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC    2606</a> mówi, że domeny te powstały dla linkowania przykładów, dokumentacji itp.</p>
<blockquote><p>   The Internet Assigned Numbers Authority (IANA) also currently has the following second level domain names reserved which can be used as examples.</p></blockquote>
<p>Najlepszym rozwiązaniem wydaje się być połączenie wszystkich technik. Postanowiłem <a href="http://athlan.vgroup.pl/code/html-antispam">napisać kawałek kodu</a> jako przykład. <u>Reszta zależy od Waszej kreatywności</u>.</p>
]]></content:encoded>
			<wfw:commentRss>http://athlan.pl/obrona-przed-spamem/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
