Archiwalia dla 08.2005

Przezorni złodzieje

Wczoraj miałem zaszczyt przebywać na przyjęciu towarzyskim (łac. bibos) w charakterze alterimprezowicza, jako że w tym samym czasie w tym samym miejscu odbywało się inne party (mechanika kwantowa się kłania). Gości z tej drugiej imprezy nie znałem, a byli nieco młodsi - mediana wynosiła 17 lat. Zachowywali się dość chaotycznie i przejawiali wszelkie cechy dobrze wytrenowanego kamikaze.

Zdenerwowałem się nieco, kiedy odkryłem, że ktoś majstrował przy moim plecaku. Szybka procedura sprawdzania spójności środowiska - portfel jest, zawartość się zgadza; notebook jest; segregator z płytami nie zginął; telefon mam zawsze przy pasku. Po co więc dłubać w cudzym plecaku? Okazało się, że ktoś zabrał kilka tabletek Ibupromu (ślad - brakująca wata w słoiczku). Drugie znalezisko rozbawiło mnie jednak dalece mocniej.

Zginęła jedna prezerwatywa. :D

Przedwczesna optymalizacja

Patrzę dziś z łezką w oku na kod, który napisałem w Pascalu na początku 1998. roku (czyli gdzieś w pierwszej klasie ogólniaka):

procedure putpixel(x,y:word;color:word); assembler;
asm
  mov cx,640*2
  mov bx,x
  cmp bx,639
  ja @noput
  mov ax,y
  cmp ax,479
  ja @noput
  add ax,topscreeny
  mul cx
  shl bx,1
  add ax,bx
  adc dx,0
  mov si,ax
  cmp dl,curbank
  je @bankok
  call dword ptr bankswitch
  mov curbank,dl
@bankok:
  push $a000
  pop es
  mov ax,color
  mov es:[si],ax
@noput:
end;

I śmiać mi się chce, kiedy w oknie obok widzę kod, który dziś mam po kimś poprawiać:

for ($i = 0, $j = 0; ; )
{
	$tab[$i++] = $title[$j];
	$tab[$i++] = $content[$j++];
	if ($j >= $limit)
		break;
}

Nie wiedziałem, że czas procesora jest aż tak cenny w zagnieżdżanych językach skryptowych. Pół biedy z tym, ale widziałem już takie krzaki na ekranie, że sam autor by mi dziś nie powiedział, co kod robi bez pomocy debugowania.

The Daily WTF, part 3

No nie mogę. Ex aequo dzisiejszą nagrodę otrzymuje również zespół dinfo.pl. Jako ich partner, jesteśmy zmuszeni do korzystania z ich API do wykonywania wszelkich operacji na domenach. Właśnie wpisałem o-umlaut do formularza wyszukiwania domen i system zmarł z komunikatem zły assert. Szukałem błędu po swojej stronie, ale ich serwis okazywał się reagować tak samo. Zajrzałem przeto w otchłań bezdenną i wydobyłem z jej czeluści kod ichniej implementacji punycode, czyli systemu tłumaczenia nazw domen na obowiązujący standard IDN:

function punycode($tryb,$tekst)
{ //tryby: "Encode"/"Decode"
	$iso = array(177,230,234,179,241,243,182,188,191,
	161,198,202,163,209,211,166,172,175);

	$uni = array(261,263,281,322,324,243,347,378,380,260,
	262,280,321,323,211,346,377,379);

	$tekst = iconv('utf-8', 'iso-8859-2', $tekst);

	$print_ascii ="\n\n\n\n\n\n\n\n\n\n\n\n\n\n".
	"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n !\"".
	"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK".
	"LMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrs"
	"tuvwxyz{|}~\n";

	$domenatab=explode(".",$tekst);
	//podzial domeny na tablice(rozdzial z kropek)

	if ($tryb=="Encode") {
		$output="";
		for ($l=0;$l<count($domenatab);$l++){
		//licznik slow miedzy kropkami
			$tekstuni=array();
			$tekst=$domenatab[$l];
			$polskieznaczki=false;
			for ($i=0;$i<strlen($tekst);$i++) {
				$znaleziony=0;
				for ($j=0;$j<count($iso);$j++) {
					if (ord($tekst[$i])==$iso[$j])  {
						$tekstuni[$i]=$uni[$j];
						$znaleziony=1;
						$polskieznaczki=true;
						break;
					}
				}
				if ($znaleziony==0) {
					$tekstuni[$i]=ord($tekst[$i]);
				}
			}

			// Encode: //
			if ($polskieznaczki==true) {
				$output .= "xn--".
				$this->punycode_encode(count($tekstuni), $tekstuni);
			} else {
				$output.=$domenatab[$l];
			}
			if ($l!=count($domenatab)-1) {
				$output.=".";
			}
		} //for l=0….
		$output_length=strlen($output);

		// Convert to native charset and output: //
		for ($j = 0;  $j < $output_length;  ++$j) {
			$c = ord($output[$j]);
			if ($c >= 0 && $c <= 127 && isset($c)) {
				if (ord($print_ascii[$c]) == 0) {
					echo "invalid_input";
				}
				$output[$j] = $print_ascii[$c];
			} else {
				echo "zly assert (246)";
				exit;
			}
		}
		return str_replace("\r","",$output);
	}
	// [..]
}

Przejrzyściej napisanego kodu nie napisałem, mimo wszystko udało mi się dostrzec, że:

  • system potrafi obsługiwać tylko domeny podane w Latin2
  • ma na sztywno wpisanych kilka polskich znaków (pierwsze dwie tablice), a inne skrupulatnie olewa
  • nie ma nic wspólnego z oryginalnym algorytmem punycode (tej części nie wklejałem), którego implementację w PHP na licencji GPL można wygooglać w 10 sekund
  • prześlicznie obsługuje błędy, nie ma to jak zabić skrypt w przypadku wprowadzenia nieobsługiwanych znaków do formularza

The Daily WTF, part 2

Jako, że data już technicznie rzecz biorąc inna, mogę zamieścić kolejny WTF.

Pamiętajcie, środków ostrożności nigdy dość, dlatego ważną rolę pełni security by obscurity.

<?
if ($_COOKIE["login"] != "admin")
	print ‘<div id="admin" style="display:none;">’;
else
	print ‘<div id="admin">’;
?>
	<!– tutaj linki do usuwania użytkowników –>
</div>

The Daily WTF

Dzisiejszą nagrodę otrzymuje ode mnie autor poniższego kodu:

if (sizeof($tab) > 0 ? true : false)
{
	// [...] - tutaj kod
}

Autora i źródła nie podam z podziwu dla tak zaawansowanych konstrukcji.

PS: Podobno dzisiaj Windows 95 (Windows Chicago) obchodzi swoje 10. urodziny.

Inżynieria nieprogramowania

Studenci i hobbyści lubią pisać kod dla samej zabawy pisania kodu. Stąd różnorodność rozwiązań w każdej niemal kategorii oprogramowania. Dzięki temu klony Uniksa są dziś najbardziej elastycznymi systemami. Sytuacja wygląda jednak nieco inaczej w przypadku oprogramowania komercyjnego.

Każdy chce się pochwalić swoimi umiejętnościami i chętnie napisałby wszystko od zera, ale w odróżnieniu od oprogramowania OpenSource, w komercyjnych rozwiązaniach obowiązują terminy, a głównym czynnikiem napędzającym całą zabawę są pieniądze. Ważne nie jest popisanie się, ale efektywne zgospodarowanie czasu, stąd pisanie własnych rzeczy często jest niepożądane.

Zanim zabierzesz się za pisanie własnego modułu, silnika czy jakiegokolwiek elementu oprogramowania, rozejrzyj się po rynku. Sprawdź czy istnieją gotowe rozwiązania, z których mógłbyś skorzystać i zaoszczędzić czas. Jeśli nie uda ci się znaleźć nic, co pasowałoby do twojego projektu, zainteresuj się tymi, które nie pasują. Sprawdź, jaką mają bazę użytkowników, czy znane są jakieś błędy, z jakimi problemami borykają się ci, którzy im zaufali i czy nie żałują swojej decyzji. Zobacz, czy nie da się ich zaadaptować za pomocą minimalnych zmian.

Pisanie własnego systemu powinno być ostatnim podejmowanym krokiem. Jeśli nie znajdziesz nic, co mogłoby ci zaoszczędzić czasu, to zaoszczędź go sobie sam - przeznacz czas na zaplanowanie swoich działań zanim napiszesz choćby linijkę. Zastanów się nad architekturą, porównaj ją z istniejącymi i skorzystaj z poznanej wcześniej listy problemów. Pisanie oprogramowania, które nie będzie się nadawało do wdrożenia mija się z celem, tak samo jak napisanie od zera kolejnego pakietu o identycznej funkcjonalności.

Pamiętaj, że prawdopodobnie nie będziesz jedyną osobą dbającą o ów kod - zaplanuj go rozwojowo i tak, żeby wprowadzanie zmian nie wymagało przepisywania dużych fragmentów kodu. Przygotuj choćby krótką dokumentację - twoi współpracownicy nie będą mieli czasu na czytanie całości kodu za każdym razem, kiedy potrzebują z niego skorzystać. To jest właśnie największa zaleta korzystania z szeroko stosowanych rozwiązań - problemy za ciebie rozwiązują inni. Ty możesz skupić się kodzie funkcjonalnym, a nie na poprawianiu używanych narzędzi i bibliotek.

Reasumując: w pracy codziennej pisze się masę nowego kodu, ale staramy się ograniczać do tego, którego napisanie jest nieodwołalne. Cała reszta to koszty, które dla pracodawcy nie przynoszą żadnego zysku (poza kodem, o który trzeba dbać, co pochłania dalsze ilości czasu, a w efekcie przekłada się znów na pieniądze).

Offtopic: Relokacja dyfuzyjna

Dzisiaj (zmotywowany przebudzeniem o 17:00 po wczorajszej imprezie) sformułowałem w końcu swoją teorię z dziedziny modelowania zjawisk fizyki cząsteczkowej.

Relokacja Dyfuzyjna (RD) to zjawisko powszechne w przyrodzie. Jego zaistnienie jest nieprzewidywalne, jednak pewne czynniki mogą wpłynąć dodatnio na szansę jego wystąpienia. Kluczową rolę pełni tutaj dostarczenie odpowiedniego kwantu energii w postaci alkoholu etylowego i produktów z grupy wyrobów tytoniowych. Zaistnienie RD możemy stwierdzić dopiero po fakcie, poprzez analizę symptomów. Proces RD charakteryzuje się tym, że ciało ulega rozszczepieniu na pojedyncze cząsteczki. Każda z nich porusza się do celu innym torem, ciężko określić wzór pozwalający dokładnie wyznaczyć trasę poruszania się poszczególnych cząsteczek, wiemy natomiast że ich tory podobne są do tych pokonywanych przez ładunki elektryczne podczas wyładowań atmosferycznych bądź do torów kreślonych przez węże na piasku. Niektóre cząsteczki zdają się wykazywać własności magnetyczne względem krawężnika. Proces ponownej asemblacji materii jest dość długotrwały, co widać szczególnie dobrze na krótkich odcinkach. Te, których pokonanie pieszo zajmuje 20 minut, mogą zająć RD ponad godzinę. Co ciekawe, można w ten sposób pokonać odcinki teoretycznie nie do przejścia na nogach.
Zaliczamy do nich: jeziora, rzeki, tereny bagienne, wysoko porośnięte łąki, a także płoty i mury. Druga charakterystyczna, poza nadmiarowym upływem czasu, cecha to wielodrożność poruszających się paralelistycznie cząstek. Stąd często, gdy pytamy kilku osób o trasę, którą wracaliśmy do domu, otrzymujemy kilkanaście sprzecznych wersji. Nazywamy to częściową, nabytą zdolnością bilokacji. Jest ona tylko tymczasowym efektem ubocznym RD i organizm traci tę właściwość po znalezieniu się w łóżku, bądź innym dogodnym do spania miejscu (w tym ławki, trawniki).

DistroDev.org

Aredridel (PLD Linux) i xentac (Arch Linux) ogłosili niedawno otwarcie dość niecodziennego portalu. Distro Development Talk ma na celu skupienie społeczności związanej z rozwojem różnych dystrybucji. Ta różność jest tu kluczowa, bo serwis nie ma służyć za helpdesk (nie, to nie jest miejsce, gdzie użytkownicy mogą zadawać pytania), ma być platformą do swobodnego mind stormingu. Dzieląc się swoimi opiniami mamy okazję poznać sprawę z punktu widzenia innych dystrybucji, możemy również rozszerzyć swoją wiedzę, czytając o pomysłach i problemach innych. Póki co, serwis jest w powijakach i dopiero zbiera grono uczestników, stąd niska aktywność, dlatego zachęcam do rejestracji. Podkreślam jeszcze raz, że jest to usługa dla deweloperów i zadawanie tam pytań jak zrobić jest kiepskim pomysłem.

Smarty

Coraz częściej spotykam się z kodem pisanym na szybko przez ludzi mających nikłe pojęcie o swoim podstawowym narzędziu pracy - języku PHP. Kawałki kodu wyglądają tak:

while($row = mysql_fetch_array($result))
{
	print "<tr><td>" . $row['imie'] .
	"</td><td>" . $row['nazwisko'] .
	"</td><td>" . date("d.m.Y", $row['data_urodzenia']) .
	"</td></tr>\n";
}

Kod paskudny i na ogół wyświetlanie wyników zajmuje przynajmniej 3/4 pliku, który odpowiada za ich pobieranie z bazy. Znacznie wygodniej byłoby oddzielić widok od kontrolera i pozwolić temu pierwszemu troszczyć się o formatowanie danych (date(...);), a temu drugiemu zająć się tylko wydobywaniem niezbędnych danych.

Tutaj pojawiają się szablony. Równie kiepskim pomysłem, jak wypluwanie kodu HTML bezpośrednio przez kontroler, jest pisanie własnego silnika szablonowego. Znacznie lepiej stosować powszechnie uznane narzędzia, jak Smarty - system inteligentnych szablonów.

Powyższy przykład przepisany za pomocą Smarty składałby się z dwóch plików. Pierwszy plik, z kontrolerem wyglądałby tak:

$smarty = & new Smarty();
$dataset = array();

while($row = mysql_fetch_assoc($result))
{
	array_push($dataset, $row);
}

$smarty->assign('dataset', $dataset);
$smarty->display('data-template.html');

Drugi zaś, zawierający sam szablon:

<table>
{section name="row" loop=$dataset}
	<tr>
		<td>{$dataset[row].imie}</td>
		<td>{$dataset[row].nazwisko}</td>
		<td>{$dataset[row].data_urodzenia|
		date_format:"%d.%m.%Y"}</td>
	</tr>
{/section}
</table>

Największa zaleta tego podejścia - czytelność i uniezależnienie silnika strony od jej wyglądu.

Druga zaleta - reużywalność raz wykonanych szablonów, załóżmy bowiem, że strona zawiera formularz, który należy wypełnić i który jest następnie walidowany po stronie serwera pod kątem poprawności pól. Co jeśli pola nie są wypełnione poprawnie? Niektóre serwisy wyświetlają błąd i przycisk wstecz - niezbyt elegancka metoda, biorąc pod uwagę, że IE skrupulatnie czyści pola formularza przy wracaniu do strony z historii. Można jednak przygotować jeden formularz:

<form action="/" method="post">
	<fieldset>
		<legend>Dane osobowe</legend>
		<p>Imię:
		<input type="text" name="imie"
		value="{$smarty.post.imie}"></p>
		<p>Nazwisko:
		<input type="text" name="nazwisko"
		value="{$smarty.post.nazwisko}"></p>
		<p>Email:
		<input type="text" name="email"
		value="{$smarty.post.email}"></p>
	</fieldset>
</form>

Następnie wywołać go dwukrotnie - raz przy konieczności jego wypełnienia, drugi raz w funkcji sprawdzającej jego poprawność, jeśli zaistnieje taka potrzeba. Pola pozostaną wypełnione, a użytkownik ograniczy się do poprawienia tylko tych danych, które wprowadzone zostały błędnie.

Trzecia zaleta - system taki pozwala na jednoczesną pracę programistów i webmasterów, szablony można rozwijać równolegle z kodem, co znacznie przyspiesza rozwój serwisu, a czas realizacji jest często kluczowym kryterium przy negocjacjach z klientami.

Strip #017: Dni wolne są najgorsze

Urlop to taki okres, kiedy możemy w końcu robić to, na co mamy ochotę.

Tutaj w graficznych przeglądarkach wyświetla się komiks