Archiwum

Posty oznaczone ‘PHP’

Vim: dopełnianie dowolnego tekstu (np. słów kluczowych PHP)

Styczeń 13th, 2010 zergu Brak komentarzy

Vim posiada dwa bardzo przydatne skróty klawiszowe: ctrl-n oraz ctrl-p. Obydwa służą do dopełniania słów, pierwszy zaczyna szukać za kursorem, drugi przed nim. Domyślnie Vim korzysta ze słów z bieżącego pliku oraz z wszystkich otwartych buforów.

Można jednak tę funkcjonalność rozszerzyć dodając własny słownik. Sam słownik, to zwykły plik tekstowy, w którym słowa są oddzielone białymi znakami. Posiada on jednak dość dziwne ograniczenie — czyta tylko 511 znaków w jednej linii, dlatego nie wolno przesadzać z ich długością.

Słownik może być np. takiej postaci:

dzięciowodoronaftalen
dziewięciodoronaftalen
antypolonazsiderywizacjaimigtenwadsioptaweertyytna
fluorochlorowęglanowodoroazotanocydy
konstantynopolitańczykowianeczka
cześć

Aby skorzystać ze słownika należy dodać 2 linijki (właściwie może być jedna) do ~/.vimrc:

set dictionary+=~/sciezka/do/slownika
set complete+=k

Pierwsza komenda ładuje słownik, druga sprawia, że możemy go przeszukiwać za pomocą wcześniej wspomnianych skrótów (domyślnie trzeba byłoby wciskać ctrl-x-k). Słowników może być wiele.

Słownik możemy sobie utworzyć na bazie plików Vima ze składnią do języków programowania (pod debianopodobnymi w katalogu /usr/share/vim/vim72/syntax/). Należy tutaj jednak pamiętać o ograniczeniu długości linii. Dlatego dla samego PHP udostępniamy gotowy słownik słów kluczowych.

Kategorie:Vim Tagi:

Jak przycinać i skalować obrazki?

Kwiecień 17th, 2009 eshaem 1 komentarz

Odwiecznym problemem przy wyświetlaniu obrazków użytkowników jest przycinanie i skalowanie ich w taki sposób, który pasuje do wyglądu serwisu. Problem na pierwszy rzut oka może wydawać się dość trudny ze względu na różnorodność obrazków, które może wysłać użytkownik, posiada on jednak proste i eleganckie rozwiązanie.

lena

Lena - obiekt naszych testów

Na potrzeby wypisu założmy, że zajmujemy się przeskalowaniem powyższego obrazka z rozmiarów 300 x 300 px na 200 x 100 px. Można pomyśleć o najprostszym rozwiązaniu, czyli przeskalowaniu albo wycięciu odpowiedniego kawłka z oryginalnego obrazka. Żadne z tych rozwiązań nie jest jednak dobre, pierwsze nie poradzi sobie z plikami, w których stosunek szerokości do długości jest inny niż docelowy, drugą metodą wytniemy jedynie fragment z obrazka pomijając być może istotną część interesującą użytkowników naszego serwisu. (W szczególności problem pojawia się problem przy dużych obrazkach, którą część wycinać? Przedstawiony poniżej przykład jest nieco tendecyncyjny ze względu na małą wielkość obrazka.)

Skalowanie

Skalowanie

Wycinanie

Wycinanie

Potrzebujemy więc rozwiązanie, które pozwoli uwzględnić różny stosunek wysokości do szerokości obrazka dostarczonego przez użytkownika, oraz umożliwi pokazanie jego jak największej zawartości. Najlepiej więc będzie jeżeli będziemy tylko ścinać obraz z góry i dołu, lub lewej i prawej a następnie skalować go do żądanego rozmiaru. Niech iw i ih oznaczają odpowiednio szerokość i wysokość obrazka wejściowego, natomiast ow i oh analogicznie dla obrazka wyjściowego. Obliczamy stosunki szerokości do wysokości dla obrazków:

  • Ri = iw/ih – w naszym przypadku wynosi on 300/300 = 1
  • Ro = ow/oh – w naszym przypadku wynosi on 200/100 = 2

Ponieważ Ro jest większy niż Ri, czyli w obrazku wyjściowym przypada więcej pikseli szerokości na piksel wysokości, to powinniśmy wyciąć możliwe najmniejszy kawałek góry i dołu z oryginalnego obrazka tak aby uzyskać w nim współczynnik Ro. Wymiary pasów, które powinniśmy wyciąć obliczamy następująco:

  • dh = iw/ro – w naszym przypadku 300/2 = 150

Ponieważ sumaryczna szerokość pasa wynosi 150, to przytniemy z góry i dołu pasy o szerokości 300 i wysokości 75 i skalujemy to co zostało do rozmiaru 200 x 100 px, przyjmując optymistycznie, że na skraju obrazka znajdują się zawsze najmniej istotne treści.

Fragment obrazka do przeskalowania

Fragment obrazka do przeskalowania

Wynik działania metody

Wynik działania metody

Sytuacja gdy Ro jest mniejsze od Ri jest analogiczna, nie będę więc jej omawiał. Przedstawiony algorytm jest bardzo prosty i w pseudokodzie wygląda następująco:

Ri = iw / ih
Ro = ow / oh

if (Ri  <= Ro)
{
new_h = ow / Ro
segment = (ih-newh)/2
zetnij pas o wysokości segment z góry i dołu i przeskaluj do rozmiarów ow x oh px
}
else
{
neww = Ro * ih
segment = (iw – neww) / 2
zetnij pas o szerokości segment z lewej i prawej i przeskaluj do rozmiarów ow x oh px
}

Przy implementacji  powyższego rozwiązania w języku PHP do ścinania i skalowania przydatna może okazać się funkcja imagecopyresampled().

Bardzo ciężki quiz PHP

Kwiecień 2nd, 2009 Mistrz Wu 3 comments

W ramach rozluźniania się zrobiliśmy sobie test wiedzy na temat PHP. Test może wydawać się trudny, ale na doświadczonym pisarzu PHP nie zrobi wrażenia.

Taką tabelkę sobie uknuliśmy. Należy odpowiedzieć na pytanie: jaką wartość zwrócą poszczególne wyrażenia z tabeli? Obok nasze typowania i ukryta odpowiedź prawidłowa (zaznacz tabelę/tekst, aby zobaczyć, co tam się kryje).

Wyrażenie Reinmar Mr. Z mcv Odpowiedź prawidłowa
0 == 'abc' T F T T
'0' == 'abc' F F T F
'0' == '0000' T F T T
'000' == 'abc' F F F F
'000' == '00000' F F F T
false != null F F F F

Tak więc przypominamy o istnieniu operatora ===. Niech się koduje!

Kategorie:PHP Tagi:,

Walidacja jednego pola formularza w symfony

Marzec 31st, 2009 reinmar 2 comments

Natknąłem się ostatnio na problem. Otóż wszystkie formularze walidujemy przy wykorzystaniu symfoniowych Formsów. Standardowe użycie podczas aktualizacji obiektu Place wygląda następująco:

$this->form = new PlaceForm ($place);
$this->form->bind ($request->getParameter ('place'));

if ($this->form->isValid())
{
	$place = $this->form->updateObject();
	$place->save();
}

Co jednak kiedy nie chcemy walidować całego formularza? Np. w przypadku kiedy używając InPlaceEditora zmieniamy jedno pole. Podpowiem, że próba wysłania tylko tego jednego pola i wykorzystaniu tej samej akcji skończy się wyzerowaniem pozostałych pól.

Dokumentacja symfony niestety milczy na ten temat (choć przyznam się, że nie szukałem jakoś strasznie dokładnie), a wiemy chyba wszyscy co jest jej największą bolączką – opisanie tylko kilku funkcjonalności poszczególnych modułów. Jak się chce czegoś więcej, to droga wolna – szukajcie sobie sami w kodzie.

Tak też postąpiłem. I po koszmarnie długim czasie odkryłem, że do walidowania każdego z pól służy metoda o intuicyjnej nazwie… clean(). Przykład walidacji jednego pola:

$form = new PlaceForm($place);
try
{
	$cleaned = $form->getValidator($field_name)->clean($value); #1

	$place->setByName($field_name, $cleaned, BasePeer::TYPE_FIELDNAME); #2
	$place->save();
}
catch (sfValidatorError $e) #3
{
	#Obsługa błędu
}
  1. Pobranie odpowiedniego walidatora oraz wywołanie na nim metody clean(), która w przypadku, gdy pole ma poprawną wartość zwraca je. A w przypadku błędu wyrzuca wyjątek sfValidatorError,
  2. Zapisanie wyczyszczonej wartości tego pola do obiektu,
  3. Kod obsługujący przypadek niewalidującego się pola.

Mam nadzieję, że ten krótki artykuł pozwoli komuś zaoszczędzić trochę czasu.

Wpis opublikowałem także na prywatnym blogu reinmar.jogger.pl.

Sortowanie obiektów po dacie utworzenia

Luty 23rd, 2009 zergu 1 komentarz

Czasami istnieje potrzeba posortowania obiektów wg własnych kryteriów — dajmy na to w przypadku, gdy w jednej tablicy mamy obiekty różnych typów. Aby jednak sortowanie mogło mieć sens, potrzebne jest jakieś wspólne pole. W tym przykładzie chcemy sortować po dacie utworzenia, więc z założenia wynika, że obiekty będą miały pole created_at.

Do sortowania może wykorzystać funkcję usort:

bool usort ( array &$array , callback $cmp_function )

Jako argumenty przyjmuje ona tablicę i funkcję do porównywania (tzw. callback).

Funkcja porównująca przyjmuje za argumenty wartości do porównania (tutaj: obiekty) i zwraca -1, 0 lub 1 aby określić czy pierwsza wartość jest odpowiednio mniejsza, równa czy większa. W naszym przypadku znaki są zamienione by posortować „od najnowszego” oraz, dla uproszczenia, pomijamy zwracanie zera, ponieważ jego wystąpienie i tak jest mało prawdopodobne, a ponad to nie istnieje kolejne kryterium porównania, więc w przypadku takiej samej daty, kolejność dwóch obiektów jest dowolna:

public static function compare_by_date ($o1, $o2)
{
    return ($o1->getCreatedAt ('U') <= $o2->getCreatedAt ('U')) ? +1 : -1;
}

Metoda korzysta z dość typowego w aplikacjach Symfony (opartych na Propelu) gettera getCreatedAt, który przyjmuje format daty w takiej samej postaci jak funkcja PHP date. W tym przypadku jest to tzw. Unix epoch oznaczający liczbę sekund od początku roku 1970. A sekundy (czytaj: liczby całkowite) porównuje się już bez problemu.

Mając gotową taką funkcję (tutaj jako metoda klasy std) możemy posortować tablicę obiektów poprzez wykonanie:

usort ($images_and_videos, array ('std', 'compare_by_date'))

Kategorie:PHP Tagi:,

Vim: powolna edycja plików PHP

Styczeń 26th, 2009 zergu Brak komentarzy

Vim potafi bardzo spowolnić (szczególnie poruszanie kursorem) podczas edycji plików PHP, co jest spowodowane najprawdopodobniej dopasoywaniem nawiasów (aby podświetlić parę). Jeśli ten ficzer nie jest dla krytyczny do pracy, warto go wyłączyć dodając w swoim .vimrc linię:

let loaded_matchparen = 1

Oczywiście wciąż można używać kombinacji shift-5, aby „ręcznie” odnaleźć nawias z pary.

Kategorie:Vim Tagi:,

Epic: ALERT w PHP i niespodziewane białe ekrany śmierci

Grudzień 22nd, 2008 Mistrz Wu 1 komentarz

Zacznę od tego, że problemu nie zdołałem rozwiązać, gdyż zwyczajnie brakowało mi czasu i nerwów. Zamiast tego, zrobiłem obejścia, które… no, działają. A dlaczego? Nie wiem. Może jakiś doświadczony czytelnik zostawi odpowiedź w komentarzu…

Tak więc onegdaj, podczas wesołej pracy z PHP, zdarzyło się kilka razy, że przy prostej i klarownej operacji na dobrze określonych wartościach, PHP rzucał białym ekranem śmierci, a w logach pojawiało się niejasne:

ALERT - canary mismatch on efree() - heap overflow detected (attacker '127.0.0.1', file '/home/mcv/htdocs/cośtam/dispatch.php')

Czasami tylko zamiast efree było erealloc.

Chwila kombinowania wykazała, że taki efekt daje następująca linia:

$ids = array_merge ($this->f1(), $this->f2(), $this->f3());

Nazwy metod zostały zmienione dla większej czytelności. Wartości zwracane przez metody były najzwyklejszymi, PHP-owymi tablicami zawierającymi jedynie liczby całkowite. Liczba? Na pewno nie większa niż około 5800 w sumie. 5800 intów to dużo czy jak?

Po straceniu stosownej ilości nerwów, wpadłem na to, żeby trochę PHP-a oszukać. Dodałem zmienne pomocniczne:

$a = $this->f1();
$b = $this->f2();
$c = $this->f3();
$ids = array_merge ($a, $b, $c);

Magia! Wszystko zaczęło działać. Czy PHP znowu coś przede mną ukrywa?

W międzyczasie Pan Google pomógł skojarzyć dziwne wpisy w logach z „utwardzaczem” PHP, tj. Suhosinem, który ma za zadanie uodparniać dziurawego PHP na różne ataki włamywaczy. I niestety tylko tyle. Trop się urwał, a ja nie wnikałem, skoro obejście zadziałało. Pewnie coś z parserem PHP nie tak (jak zwykle), albo Suhosin narobił jakiegoś bałaganu. Łorewer.

Kilka dni później, po dodaniu nowego argumentu do zupełnie innej metody:

ALERT - canary mismatch on efree() - heap overflow detected (attacker '127.0.0.1', file '/home/mcv/htdocs/cośtam/dispatch.php')

Co? Że jak? Niniejszy kod:

public function render ($partial_name, array $locals = array())
{ … }

zastąpiłem nastepującym:

public function render ($partial_name, array $locals = array(), array $options = array())
{ … }

Od tej pory biały ekran śmierci (+wpis w logu) powodowały wszelkie możliwe rodzaje wywołań:

$this->render ('xyz');
$this->render ('xyz', array ('var' => 'value'));
$this->render ('xyz', array ('var' => 'value'), array());
# itp.

Znów zacząłem tracić czas i nerwy, aż w końcu dobra dusza Zergu zasugerował, żebym zrobił to samo co ostatnio. Chwila szukania w pamięci dała poniższy efekt.

function render ($partial_name, array $locals = array(), array $options = array())
{
    $this->render_old ($partial_name, $locals, $options);
}

function render_old ($partial_name, array $locals = array(), array $options = array())
{ … }

Magia, proszę Państwa! Działa! Nigdy nie lekceważcie potęgi nic-nie-robiącej funkcji opakowującej. Ani niepotrzebnych zmiennych tymczasowych. Bo mogą jednak okazać się potrzebne. A może ktoś zna wytłumaczenie dla tego bezsensownego zachowania PHP? Bo ja tego zupełnie nie rozumiem. Ale wiem już, że gdy zobaczę czyjś kod podobny do powyższego, to wcale nie z programisty będę się śmiał, a z czego innego… :^)

Dobranoc państwu i Wesołych Świąt.

Kategorie:PHP Tagi:, , , ,

Connection: close + Apache Benchmark

Grudzień 19th, 2008 Mistrz Wu Brak komentarzy

Testowanie apache-benchmarkiem (/usr/sbin/ab) jest niewątpliwie łatwe, proste i przyjemne, nie licząc rozczarowania wynikami. Znakomicie spisuje się w przypadku Rails (nie mam pojęcia dlaczego), ale w aplikacyjach PHP może nieco zaskoczyć.

Też nie wiem dlaczego, ale wydaje mi się, że jest prawdopodobnym iż…

Otóż PHP, słusznie zresztą, przy braku nagłówka Connection czeka na kolejne zapytania na tym samym połączeniu (zakłada domyślnie: keep-alive), a ab sam z siebie go nie wysyła i… zwiesza się na iluś tam otwartych połączeniach, na których nic się nie dzieje (ab chyba czeka aż to serwer się rozłączy). Niezbyt miło, bo to zaniża wyniki benczmarku i pogarsza i tak już złe samopoczucie.

Dlategoż zawsze dla pewniejszych wyników (i dobrego samopoczucia) warto dodawać ab opcje -H 'Connection: close'. Manual mówi nawet, że przełącznikiem -H można też inne nagłówki wysyłać, gdyby komuś zależało.

Czy to jednak znaczy, że Rails w wersji cokolwiek starej, bo 2.1.1, domyślnie zakłada Connection: close? To by było niesłuszne, lecz może być wytłumaczone jednowątkowością Rails. Tego niestety, już mi się nie chciało inwestygować, bo święta idą.

Tak więc szczęśliwego nowego jajka i co tam jeszcze!

Kategorie:Różne Tagi:, , ,