czwartek, 27 listopada 2008

Operatory post i pre-increment w PHP

Na pierwszy rzut oka prawie nie ma roznicy:
$i = 0;
while ($i < 1000000)
{
    $i++;
}



czy:

$i = 0;
while ($i < 1000000)
{
    ++$i;
}

W każdym razie wynik będzie ten sam. A jednak różnica w implementacji obu operatorów jest spora. W pierwszym przykładzie użyłem operatora post-increment, który dodaje do 1 do zmiennej $i po wykonaniu lini kodu. W drugim przypadku 1 jest dodawane przed wykonaniem lini kodu.
W większości zastosowań nie ma to znaczenia, jednak przy dużej ilości takich operacji może mieć znaczący wpływ na wydajność naszej aplikacji. A co za tym idzie, zużycie czasu procesora, energii, efekt cieplarniany ;)
Dzieje się tak, ponieważ aby wykonać $i++ jest konieczne wykonanie kopii zmiennej. Czyli w efekcie mamy "dwie" zmienne $i. Jedna przechowuje poprzednią wartość a druga przechowuje wartość po dodaniu jedynki.
Zrobiłem pare testów szybkości na powyższym kodzie. Czas wykonania pętli z zastosowaniem $i++ jest o 10% większy niż wykonanie tej samej pętli z zastosowaniem ++$i.
Warto na takie sprawy zwracać uwagę, szczególnie w projektach w których przetwarzamy duże ilości danych.

W następnych postach postaram się opisać trochę więcej zagadnień związanych z wydajnością oraz optymalizacją kodu.

poniedziałek, 3 listopada 2008

Continuous integration - Bitten

Pisanie testów (unit testów, testów funkcjonalnych, selenium) stało się integralną częścią większości projektów. Jednak uruchamianie dużej liczby testów może zajmować sporo czasu. Ponadto jeżeli nad projektem pracuje kilku programistów może pojawić się typowy problem, że u kogoś działa a u kogoś innego nie. Warto więc stworzyć środowisko testowe, na którym testy będą uruchamiane automatycznie.
Jeżeli korzystasz z Traca i Subversion możesz skorzystać z Bittena (http://bitten.edgewall.org/).
Poprzez zdefiniowane regułki Bitten aktualizuje kod w środowisku testowym (svn update), uruchomia skrypty migracji bazy, konfiguracji oraz uruchamia testy.
Procedura jest uruchamiana przy każdej zmianie kodu a raport jest dostępny poprzez Traca (http://bitten.edgewall.org/build/trunk)

poniedziałek, 1 września 2008

Struktura drzewa w Symfony

Na devzone (http://dev.mysql.com/tech-resources/articles/hierarchical-data.html) MySQLa znajduje się artykuł o tworzeniu hierarchicznych struktur danych. Popularnym zastosowaniem takiej struktury jest menu strony internetowej, gdzie mamy do czynienia z drzewem linków (kategorii).
W artykule omówione są dwa podejścia Adjacency List Model i Nested Set. Pierwsze doskonale sprawdza się w przypadku 2, no góra trzech zagnieżdżeć ale jest łatwiejsze do zaimplementowania. Nested Set sprawdza się gdy chcemy jednym zapytaniem pobrać całe drzewo lub całą ścieżkę.

Jeżeli chcemy skorzystać z Nested Net w projekcie opartym o symfony, należy w strukturze danych (schema.yml), ktore kolumny zawierają TREE_LEFT, TREE_RIGHT, TREE_SCOPE, np:

categories:
_attributes: { phpName: Category, treeMode: NestedSet}
id:          ~ #symfony default
lft:         { type: integer, required: true, 
nestedSetLeftKey: true, phpName: Left }
rgt:         { type: integer, required: true, 
nestedSetRightKey: true, phpName: Right }
level:       { type: integer, required: true }
name:        { type: varchar(100), required: true }
photo:       { type: varchar(255) }


Nested Set jest dostępny dopiero od ostatniej wersji propela 1.3. W symfony 1.1 musimy zainstalowac plugin Propel 1.3 z repozytorium http://svn.symfony-project.com/plugins/sfPropelPlugin/branches/1.3
Z moich dotychczasowych doświadczeń mogę powiedzieć, że rozwiązanie nie jest jeszcze w pełni stabilne. W przypadku modyfikacji, przenoszenia gałęzi czasem gubiona jest struktura.

Innym rozwiązaniem dla symfony jest plugin sfPropelActAsNestedSetBehaviorPlugin. Ten plugin zdaje się być w pełni stabilny. Z kolei nie bedzie w pełni działał pod Propelem 1.3.

Podsumowując, zarówno dla symfony 1.0, jak i dla symfony 1.1 polecam sfPropelActAsNestedSetBehaviorPlugin.

środa, 20 sierpnia 2008

Could not reliably determine the server's fully qualified domain name

apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName

Zapewne taki notice widział niemal każdy. Ja się z nim spotykam prawie zawsze kiedy przychodzi do firmy nowy Junior Developer, który używa Ubuntu/Kubuntu. Oczywiście komunikat w niczym nie przeszkadza, nie zakłóca działania Apacha, ale po co.. skoro można go w bardzo prosty sposób usunąć.

Wystarczy dodać linię w pliku apache2.conf (jeżeli używasz linuxa powinien się znajdować gdzieś w /etc/apache2/apache2.conf lub /usr/local/apache2/...):
sudo vim /etc/apache2/apache2.conf
ServerName localhost

Można ustawić dowolną nazwę serwera (np ServerName blabla) ale w tym wypadku należy "domenę" blabla dodać do pliku hosts (/etc/hosts).
sudo vim /etc/hosts
127.0.0.1 blabla

Po restarcie Apacha (sudo /etc/init.d/apache2 restart) już nie ujrzymy komunikatu, a także możemy korzystać z domeny http://blabla/ w przeglądarce.

Warto przy okazji dodać, że dawno dawno temu plik hosts był jedyną formą systemu DNS jaką posługiwali się użytkownicy internetu. Przesyłano sobie plik z komputera na komputer, dzięki temu można było wpisywać w przeglądarkę nazę zamiast numeru IP. Nietrudno sobie wyobrazić do jakich rozmiarów musiał dochodził taki plik :)

Symfony, propel, struktura bazy danych, unikalne kolumny

Definiowanie unikalnych kolumn, to w zasadzie dodanie 2 linii w pliku schema.yml (schema.yml to plik, w którym definiujemy strukturę bazy w projektach opartych na Propelu / Symfony).

Mamy tabelę books:
books:
_attributes:       { phpName: Book }
id:                ~ #symfony default
title:             { type: varchar(255) }
ISBN:              { type: varchar(255) }


Chcemy teraz, aby nasza tabela nie zawierała duplikatów. Dla przykładu książek o tym samym numerze ISBN.

W tym celu dodajemy atrybut _uniques a następnie podajemy nazwę klucza books_unique_isbn oraz nazwę kolumny (lub po przecinku kolumn). W efekcie nowa definicja tabeli books:

books:
_attributes:        { phpName: Book }
id:                 ~ #symfony default
title:              { type: varchar(255) }
ISBN:               { type: varchar(255) }
_uniques:
books_unique_isbn: [ISBN]


Jeżeli chcemy aby 2 lub więcej kolumn tworzyło klucz unikalny podajemy ich nazwy rozdzielone przecinkami, np:
books_unique:    [ISBN, title]

Jak założyć nowego użytkownika MySQL przez konsole?

Umieszczam małą ściągę pt. Jak założyć nową bazę mysql i użytkownika, który będzie miał pełne prawa dostępu do tej bazy (create table, drop table, delete, update etc). Użytkownik ten jednak nie może mieć prawa nawet odczytu z innych baz na serwerze MySQLa.

Załóżmy, że projekt nazywa się owidiusz. Najpierw zakładam bazę:
create database owidiusz;

Jeżeli ta baza będzie przechowywać dane w kodowaniu UTF8, dodajemy:
create database owidiusz character set utf8 collate utf8_polish_ci;

(collate pozwala na poprawne sortowanie poslkich znaków!)

Następnie uzytkownika, nadajemy mu od razu hasło:
create user owidiusz identified by 'owidiusz123';

Nadajemy uprawnienia do bazy:
GRANT ALL PRIVILEGES ON owidiusz.* TO 'owidiusz'@'localhost';

I odświeżamy uprawnienia:
flush privileges;

I to wszystko :), można korzystać z bazy.