Desaturacja w PHP

Ostatnio miałem takie małe zadanko, którego celem było wyszarzenie ikony, gdy zajdzie jakieś zdarzenie. Oczywiście nie interesowało mnie, utworzenie ręcznie szarego odpowiednika dla kolorowej ikony i wyświetlanie go gdy zajdzie taka potrzeba, dlatego poszukałem czy istnieje coś takiego w PHP,a że chwilę poświęciłem na znalezienie tego, to zanotuje tutaj ;)

Jak się okazało po chwili nieudolnych poszukiwań (uleciała mnie nazwa desaturacja, dlatego pewnie tak długo to trwało) znalazłem funkcje w php, która zrobi wszystko co mnie interesuje za „free” i nie potrzeba będzie jakiś dodatkowych libów.

Funkcja, o której mowa to

bool imagecopymergegray ( resource $dst_im , resource $src_im ,
					int $dst_x , int $dst_y , int $src_x , int $src_y ,
					int $src_w , int$src_h , int $pct )

mnogość parametrów i mnie zabija do teraz, ale po kolei
$dst_im – zasób do którego zapisany zostanie wynik działania funkcji
$src_im – zasób w którym znajduje się kolorowe zdjęcie
$dst_x – punkt x w zasobie docelowym, od którego zaczniemy wypełniać zasób
$dst_y – punkt y w zasobie docelowym, od którego zaczniemy wypełniać zasób
$src_x – punkt x w zasobie źródłowym, od którego zaczniemy pobierać punkty do przetworzenia
$src_y – punkt y w zasobie źródłowym, od którego zaczniemy pobierać punkty do przetworzenia
$src_w – szerokość zasobu (fragmentu, jaki chcemy przetworzyć)
$src_h – wysokość zasobu (fragmentu, jaki chcemy przetworzyć)
$pct – skala szarości, przez jaką „przepuścimy” źródłowy zasób, przy czym 0 to pełna skala szarości, a 100 to zerowa skala szarości (zasób pozostanie niezmieniony)
funkcja na wyjściu zwraca true/false w zależności od tego czy się udało przetworzyć zasób

I teraz już wszystko jest jasne

Na koniec mały bonus, moja klaska, którą napisałem i wykorzystuje do przetwarzania obrazków w aplikacjach PHP

class Image {
	private $image = null;
	private $imageType = null;
	private $imageTypeExtension;

	public function __construct($filename = null) {
		if($filename == null || !file_exists($filename))
			throw new Exception('File not exists');
		$this->load($filename);
	}

	public function load($filename) {
		$imageInfo = getimagesize($filename);
		$this->imageType = $imageInfo[2];
		if($this->imageType == IMAGETYPE_JPEG) {
			$this->image = imagecreatefromjpeg($filename);
			$this->imageTypeExtension = 'jpg';
		} elseif($this->imageType == IMAGETYPE_GIF) {
			$this->image = imagecreatefromgif($filename);
			$this->imageTypeExtension = 'gif';
		} elseif($this->imageType == IMAGETYPE_PNG) {
			$this->image = imagecreatefrompng($filename);
			$this->imageTypeExtension = 'png';
		} else {
			throw new Exception('Unsupported image type, only jpg, png, gif');
		}
		if($this->image === false) {
			throw new Exception('Unsupported image type, only jpg, png, gif');
		}
	}

	public function save($filename, $imageType = IMAGETYPE_JPEG, $compression = 80, $permissions = null) {
		$result = false;
		if($this->image == null)
			throw new Exception('Image error');
		if($imageType == IMAGETYPE_JPEG) {
			$result = imagejpeg($this->image, $filename, $compression);
		} elseif($imageType == IMAGETYPE_GIF) {
			$result = imagegif($this->image, $filename);
		} elseif($imageType == IMAGETYPE_PNG) {
			$result = imagepng($this->image, $filename);
		}
		if($permissions != null) {
			chmod($filename, $permissions);
		}
		return $result;
	}

	public function getWidth() {
		if($this->image == null)
			throw new Exception('Image error');
		return imagesx($this->image);
	}

	public function getHeight() {
		if($this->image == null)
			throw new Exception('Image error');
		return imagesy($this->image);
	}

	public function resizeToHeight($height) {
		$ratio = $height / $this->getHeight();
		$width = $this->getWidth() * $ratio;
		$this->resize($width, $height);
	}

	public function resizeToWidth($width) {
		$ratio = $width / $this->getWidth();
		$height = $this->getHeight() * $ratio;
		$this->resize($width, $height);
	}

	public function scale($scale) {
		$width = $this->getWidth() * $scale / 100;
		$height = $this->getHeight() * $scale / 100;
		$this->resize($width, $height);
	}

	public function resize($width, $height) {
		if($this->image == null)
			throw new Exception('Image error');
		$newImage = imagecreatetruecolor($width, $height);
		imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
		$this->image = $newImage;
	}

	public function getType() {
		if($this->image == null)
			throw new Exception('Image error');
		return $this->imageType;
	}

	public function getTypeExtension() {
		if($this->image == null)
			throw new Exception('Image error');
		return $this->imageTypeExtension;
	}

	public function setGrey($grey = 0) {
		return imagecopymergegray($this->image, $this->image, 0, 0, 0, 0, $this->getWidth(), $this->getHeight(), $grey);
	}
}

I sposób wykorzystania dla dzisiejszego przykładu

$img = new Image('kolorowy_obrazek.jpg');
$img->setGrey();
$img->save('szary_obrazek.jpg');

PHP, cURL i google

Na początek po długiej przerwie lekko, szybko i przyjemnie, głównie żeby się przełamać i zacząć coś jednak pisać ;)

Tak więc podstawy cURL w PHP na przykładzie dość oklepanym, ale też użytecznym, czyli odczytanie podstawowych danych z google.

$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, "http://www.google.pl");
$google = curl_exec($curl);
curl_close($curl);

Pierwsza linijka to otwarcie sesji cURL, następne 2 linijki to ustawienie opcji, pierwsza informuje, że będziemy chcieli odczytać zwrotną informację z danej strony, a druga ustawia jaki adres chcemy odwiedzić. 4. linijka to wykonanie połączenia i przypisanie zwrotnej informacji do zmiennej, gdybyśmy nie ustawili w 2. linijce opcji, że chcemy odczytać informację zwrotną wówczas 4. linijka wykonałaby tylko połączenie. Ostatnia linijka to zamknięcie sesji cURL.

Jak by nie patrzeć nic szczególnego, proste połączenie, teraz jednak lekka modyfikacja i stworzymy już w miarę przydatny skrypt, który będzie pobierał z google 2 chyba najczęściej sprawdzane informacje (przynajmniej przeze mnie ;)) czyli ilość podstron za indeksowanych przez google i pozycje naszej witryny na daną frazę.

$q = trim($_GET['q']);
$q = strip_tags($q);
$q = str_replace(" ", "+", $q);

$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, "http://www.google.pl/search?q=".$q);
$google = curl_exec($curl);
curl_close($curl);

Zmienna q przyjmować będzie frazę, jaką wpisujemy normalnie w wyszukiwarce (u mnie dla prostoty bez formularzy zmienna pobierana metodą GET), usunięte tagi i następnie zamiana spacji na znak „+” jak to ma miejsce w google. Gdy już mamy zapytanie, tworzymy połączenie, jak w poprzednim przykładzie z odpowiednio zmodyfikowanym adresem. Teraz nie pozostaje nam już nic innego jak przetworzyć tekst ze zmiennej google.

function ilePodstron($google) {
	$tmp = strpos($google, "resultStats") + 11;
	$start = strpos($google, " ", $tmp) + 1;
	$koniec = strpos($google, " ", $start);
	$wynik = substr($google, $start, $koniec - $start);
	$wynik = str_replace(",", "", $wynik);
	return $wynik;
}

Powyższa funkcja wyświetli nam ilość podstron za indeksowanych na daną frazę, 2. linia sprawdza pozycję wystąpienia ciągu „resultStats”, po którym następuje wypisanie ilość wyników w google, następnie szukamy pierwszej spacji po tym ciągu (dokładne określenie miejsca w którym zaczyna się liczba), potem wyszukanie kolejnej spacji (miejsce gdzie kończy się liczba ;)), 5. linia to już pobranie ze zmiennej google tylko fragmentu zawierającego liczbę, później jeszcze usuwam zbędny przecinek i zwracam wynik, prawda że proste ;) (co do informacji, ile mamy za indeksowanych podstron naszej konkretnej witryny frazę należy zmodyfikować do postaci „site:adres_strony”)
Teraz troszkę trudniejsza funkcja, czyli sprawdzenie miejsca naszej witryny w google pod daną frazą.

function ktoraPozycja($google) {
	$s = trim($_GET['s']);
	$s = strip_tags($s);
	$s = strtolower($s);
	if(!$s)
		return;
	$start = 0;
	$wynik = 1;
	while(true) {
		$start = strpos($google, "<cite>", $start);
		if($start === false)
			return;
		$start += 6;
		$koniec = strpos($google, "</cite>", $start);
		$strona = strip_tags(strtolower(substr($google, $start, $koniec - $start)));
		if(strpos($strona, $s) !== false)
			return $wynik;
		$wynik++;
	}
}

Jak wcześniej, tak i teraz dla prostoty adres interesującej nas strony pobieram metodą GET do zmiennej s. Aby nie mieć problemów później, to od razu zmieniam sobie adres na małe litery (4. linia), później sprawdzenie, czy w ogóle chce się sprawdzać stronę (czy w adresie jest zmienna s ;)). Teraz już najważniejsza część, czyli pętla, która wykonuje się, do puki w zmiennej google znajdujemy ciąg „<cite>” (w tych tagach google umieszcza adresy kolejnych witryn), linie 10. i 11. to sprawdzają i jeżeli ciąg taki nie występuje wychodzi z funkcji. Linia 13. to ustawienie zmiennej start po ciągu „<cite>”, czyli w miejscu rozpoczęcia występowania adresu witryny, linia 14. to ustawienie końca występowania adresu strony, linia 15. to wycięcie adresu strony ze zmiennej google, zamienienie adresu na małe litery i usunięcie zbędnych tagów (czasem zdarza się tag <b>). 16. linia sprawdza czy pobrany przed chwilą adres pasuje do tego jaki szukamy i jeżeli tak, to zwraca pozycję naszej strony i kończy działanie funkcji.

No to by było na tyle po powrocie ;)

A i jeżeli by ktoś nie zauważył, zaszła też zmiana w adresie bloga ;)

Szybsze php

Jak wiadomo PHP demonem szybkoście nie jest i skrypty napisane w nim też do najszybszych nie należą, tak więc dość ważne jest abyśmy sami ich dodatkowo nie zwalniali. Poniżej zaprezentuję parę uwag, jak pisać skrypty w php, aby choć odrobinę szybciej chodziły, taka mała optymalizacja php ;)

po pierwsze echo jest szybsze niż print

przy wyświetlaniu znaków należy stosować ‚ zamiast „, gdyż w cudzysłowach możemy umieścić zmienne i wyświetlana jest ich wartość, a nie nazwa jak ma to miejsce w apostrofach, tak więc tekst umieszony między ” jest dodatkow przetwarzany przez co wydłuża się praca skryptu

przy wykonywaniu pętli, która ma sprawdzić wszystkie elementy tablicy należy najpierw odczytać ilość elementów w tablicy ($ilosc = count($tablica)) i używać zmiennej w pętli (for($i = 0; $i < $ilosc; $i++)), a unikać takiego zapisu (for($i = 0; $i < count($tablica); $i++))

przy podmienianiu napisów, lepiej stosować szybsze str_replace niż prag_replace

bloki „else if ” są szybsze niż switch/case

wytłumienie błędów za pomocą @ jest bardzo wolne i lepiej nie używać, gdy nie ma ogromnej potrzeby

stosowanie $row[‚id’] jest około 7 razy szybsze niż $row[id]

preinkrementacja (++$i) jest szybsza od postinkrementacji ($i++)

inkrementacja zmiennych w obiekcie ($this->i++) jest około 3 razy wolniejsza niż zmiennych lokalnych ($i++)

mam nadzieję, że te parę uwag choć odrobine pozwoli przyśpieszyć pisane przez was skrypty ;)