EpicWEB.pl

webdesign, programowanie, phat lewt!

Ostatni projekt

ddrpl.com

Ostatnie wiadomości

[PHP] Dokumentowanie kodu

O zaletach dobrej dokumentacji nie trzeba się rozpisywać. Każdy z nas (programistów) zapewne nie raz musiał poprawiać kawałek kodu napisany przez kogoś innego lub też nawet przez siebie samego, jednak z czasem zapomnieliśmy już o co chodziło. Często wtedy patrzymy na kawałek kodu, który wygląda np. tak:

		$z = hexdec(80000000);
		if ($z & $a)
		{
			$a = ($a>>1);
			$a &= (~$z);
			$a |= 0x40000000;
			$a = ($a>>($b-1));
		}
		else
		{
			$a = ($a>>b);
		}

I zastanawiamy się co właściwie autor brał - powinien z tym skończyć.

Skoro wszyscy wiemy jak przydatna jest dokumentacja, zastanawia mnie czemu ani na Politechnice (w swoim krótkim czasie), ani na innej uczelni nie mieliśmy przedmiotów, albo chociażby kilku wykładów/ćwiczeń poświęconych na jej tworzenie? Zapewne dlatego teraz tak mało osób stosuje porządną dokumentację.

Swego czasu powstało doskonałe narzędzie/format dla programistów - Javadoc. Przy użyciu odpowiedniego formatowania komentarzy można było kilkoma kliknięciami wygenerować piękną dokumentację dla swojego projektu - mało tego, z czasem co raz więcej IDE zaczęło implementować ten format dokumentacji i używać dynamicznych podpowiedzi (np. Intelisense). Oczywiście powstawały kolejne klony i porty narzędzia - w tym port dla PHP - phpDocumentator. Jak wygląda wygenerowana dokumentacja można przekonać się np. ze samej strony projektu - oczywiście aplikacja umożliwia dowolne modyfikowanie szablonu dokumentu.

Ok, to tyle tytułem wstępu - tym razem nie będę się skupiał na obsłudze samego phpDocumentatora, ale na opisaniu formatu i podstawowych znaczników, na których operuje.

Podstawowe parametry

Javadoc (oraz pochodne) przeszukuje nasze pliki pod kątem formularzy zapisanych w konkretny sposób - mianowicie wyszukuje on wszystkie komentarze blokowe, dla przypomnienia chodzi o:

/**
 *
 */

Bloki te mogą znajdować się przed klasami, funkcjami i zmiennymi (wtedy opisują te elementy) lub też na początku pliki (w takim wypadku zwane są "page-level"). Każdy z bloków może zawierać dowolny komentarz tekstowy, wyświetlany w wyjaśnieniu danego elementu - może także zawierać dowolną ilość oraz kombinację specjalnych parametrów pomagających w formatowaniu, osadzaniu dodatkowych treści oraz podpowiadaniu parametrów wejściowych i wyjściowych funkcji

Każdy parametr zapisuje się poprzedzając go znakiem @, np:

/**
 * Poniższa funkcja robi ...
 * @PARAMETR WARTOŚĆ PARAMETRU
 */
 function foo() { (...) }

Poniżej wymienię najczęściej stosowane parametry, po pełny spis odsyłam do dokumentacji phpDocumentatora. Pragnę zwrócić także uwagę, że niektóre parametry (@abstract, @final) stały się zbędne, ponieważ z wprowadzeniem PHP5 można używać specjalnych słów kluczowych, które do tej pory zastępowały właśnie w/w parametry.

@author - parametr opisujący autora danego fragmentu kodu lub osobę, która ów kod modyfikowała, przyjmuje następującą postać:

* @author Bartosz Szczeciński <btm@anfo.pl>

@deprecated - opisuje element, który znajduje się w kodzie ze wzg. na kompatybilność wsteczną - zaleca się stosowanie nowszego odpowiednika:

* @deprecated Stara funkcja, sugeruję używać nowej wersji

@see - odniesienie do innego fragmentu kodu, np. w połączeniu z @depracated może wskazać funkcję, którą powinniśmy używać zamiast poprzedniej:

* @see innaKlasa::innaFunkcja

@example - po parametrze należy podać ścieżkę do pliku zawierającego przykład zastosowania danego kawałku kodu:

* @example przyklady/userApiTest.php

@link - podobnie jak @see, jednak odnosi nas do podanego adresu URL

* @link http://manual.phpdoc.org/HTMLframesConverter/default/

@param - jeden z najczęściej wykorzystywanych znaczników, określa typ i rolę parametru, jaki powinniśmy przekazać do funkcji. Ponieważ PHP nie posiada na razie obsługi deklaracji typów złożonych w funkcjach jako pierwszą wartość parametru podajemy jego typ

/**
 * @param int $foo określa zasadę działania funkcji
 * @param int|string $bar dodatkowy parametr, powinien to być int lub string
 */
function testFunction($foo, $bar) {(...)}

@return - kolejny z najczęściej wykorzystywanych parametrów - podobnie jak @param określa typ zwracanej przez funkcję wartości. Ponieważ funkcje zwracają tylko jedną (i to nie nazwaną) wartość, jego użycie jest dużo prostsze:

/**
 * @return int zwraca losową wartość, uzyskaną przez rzut kostką 1K6
 */
function random() {
    return 4;
}

@todo - parametr informujący nas, że podany fragment kodu jest niekompletny lub wymaga poprawek, niektóre programy IDE pozwalają nam na szybkie wyświetlenie wszystkich fragmentów kodu oznaczonych tym parametrem, więc od razu wiemy co jeszcze musimy poprawić

* @todo funckja random() powinna losować wartość za każdym razem

@var - tak, jak przy pomocy @param mogliśmy określić typ i zastosowanie parametru wejściowego wybranej funkcji, za pomocą @var możemy określić zmienne umieszczone w kodzie. Wszelkie zmienna nie opisane tym parametrem i nie posiadające odpowiedniej deklaracji zostaną opisane jako mixed

/**
 * @var int totalnie losowa liczba, serio
 */
$random = 4;

Podsumowanie

Jak już wspomniałem na wstępie - standard phpDoc definiuje jeszcze wiele dodatkowych parametrów pozwalających na lepszą organizację naszego kodu, jednak poznanie i stosowanie chociażby tej garstki podstawowych z nich pozwali nam (i naszym współpracownikom) na dużo łatwiejsze i wygodniejsze poruszanie się po kodzie - a wygenerowana z naszego kodu dokumentacja zaoszczędzi nam wielu godzin spędzonych na szukaniu odpowiednich fragmentów kodu czy też tłumaczeniu i odgadywaniu zasad działania kawałku oprogramowania, za który właśnie staliśmy się odpowiedzialni.

Za jakiś czas postaram się opisać jak skonfigurować phpDocumentator w celu wygenerowania dokumentacji z naszego projektu.

[PHP/CSS/HTML] Kilka zakurzonych przykładów

Przy okazji porządków na serwerze znalazłem kolekcję swoich starych skryptów i innych dokumentów publikowanych jeszcze na starym blogu. Ponieważ wiem, że niektóre z nich jeszcze mogą się komuś przydać postanowiłem pomóc Google, które linkuje niemiłosiernie do nie istniejących już adresów i wrzucić skrypty do sieci.

Chciałbym przypomnieć, że skrypty te mogą być nie aktualne, mogą nie odzwierciedlać aktualnie stosowanych metod, nie działać czy też być proste do odtworzenia w totalnie lepszy sposób - jest to do przewidzenia, zważywszy, że większość z nich ma ponad dwa lata.

Poniżej lista z krótkimi opisami:

Jeszcze raz - to są stare skrypt, które mogą być już nie aktualne - proszę o nie wytykanie błędów ;-)

PHP - generowanie mapy witryny

Generowanie mapy witryny - prosta, wydawało by się - rzecz. Jednak, kiedy w naszej firmie pojawiło się wolne stanowisko programisty postanowiliśmy właśnie takie zadanie dać kandydatom do zrealizowania, w celu odsiania "programistów" od programistów.

Zadanie okazało się jednak wykraczające poza wszelkie normy i na jakieś 20 osób (na przestrzeni 5 miesięcy) udało się je zrobić 5. Z tego dwie skopiowały (nie do końca działające) rozwiązania z sieci...

Twoje zadanie - mając tabelę (CREATE TABLE `_tree` (`id` INT(11), `parent` INT(11), `sort` INT(11), `nazwa` VARCHAR(255))) napisz skrypt, który wygeneruje mapę witryny w HTML (w dowolnym standardzie). Większość osób zaczyna od napisania prostego zapytania wyciągającego wszystkie wpisy z parent = 0. Nieliczni dodadzą jeszcze sort. Później, jeżeli ktoś już to zrobi przystępuje do zagnieżdżania kolejnych poziomów while() a nam ręce opadają ...

Dlatego, drogi poszukiwaczu odpowiedzi w sieci, poniżej zamieszczam dwa rozwiązania - wykuj się ich na pamięć, przyjdź do nas i pewnie dostaniesz posadkę ...

Rozwiązanie 1 - rekurencja - dużo zapytań do SQL, mało myślenia:

function lev($parent) {
	$query = mysql_query('SELECT * FROM `_tree` WHERE `parent` = '.(int)$parent.' ORDER BY `sort` ASC');
	if(mysql_num_rows($query)) {
		echo '<ul>'."\n";
		while($dane = mysql_fetch_array($query, 1)) {
			echo '<li>'."\n";
			echo $dane['nazwa']."\n"; 
			lev($dane['id']);
			echo '</li>'."\n";
		}
		echo '</ul>'."\n";
	}
}

lev(0);

Rozwiązanie 2 - jedno zapytanie, nieco więcej myślenia:

function getData($parent) {
    $query = mysql_query('SELECT * FROM `_tree` WHERE `parent` >= '.(int)$parent.' ORDER BY `parent` ASC, `sort` ASC');
    $return = array();
    while($dane = mysql_fetch_array($query, 1)) {
        $return[] = $dane;
    }
    return $return;
}

function sortData($data, $root = 0, $level = 0) {
    static $return = array();
    foreach($data as $item) {
        if($item['parent'] == $root) {
            $item['level'] = $level;
            $return[] = $item;
            sortData($data, $item['id'], $level+1);
        }
    }
    return $return;    
}
foreach(sortData(getData(0)) as $item) {
    echo str_repeat('&nbsp;', $item['level'] * 5).$item['nazwa'].'<br />';
}

Niech mi ktoś powie, że to jest mega skomplikowane? Dzisiaj pojawił się jeden człowiek, który był po 2 kierunkach na Politechnice Łódzkiej. Po godzinie nic nie zrobił. Drugi - stwierdził, że pracował w Prado (taki framework) - więc zainstalował sobie WAMP na laptopie, zassał Prado i ... po 2 godzinach wyszedł bez "do widzenia".

[PHP] Generowanie nr. dowodu osobistego

Wrzucam, bo może komuś się przyda - potrzebowałem generatora nr. dowodu osobistego do testowania aplikacji, która nie dość, że sprawdza poprawność to jeszcze nie pozwala na użycie tego samego numeru więcej niż raz. Kod PHP jest portem kodu Java z blogu Not All About Java.

function generateDO(){
        $seria = $numer = "";
        $suma=0;
        $waga = array(7,3,1,7,3);

        for($i=0;$i<3;$i++){            
                $seria .= chr(rand(0, (ord('Z') - ord('A'))) + ord('A'));
                $suma += $waga[$i] * (ord(substr($seria, $i, 1)) - ord('A') + 10);
        }
        
        for($i=0;$i<5;$i++){
                $numer .= chr(rand(0, ord('9') - ord('0')) + ord('0'));         
                $suma += $waga[$i] * (ord(substr($numer, $i, 1)) - ord('0'));           
        }
        
        return $seria . ($suma%10) . $numer;
}