Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Athlan, dodany: 10.01.2011 00:36, tagi: mysql, sql, internet

We wpisie Chmura tagów w PHP, w którym został przedstawiony problem budowy chmury tagów zapisałem przykładowe zapytanie prezentujące przykładowe dane dla klasy, które dosłownie zabija bazę danych zliczając za każdym razem ilość występowań tagów. Dostając feedbacki, zauważyłem, że problem ten jest bagatelizowany przez wiele osób. Spróbujmy zbudować bardziej optymalne rozwiązanie zarządzania strukturą danych w taki sposób, aby dane wyciągać bardzo bezboleśnie.

Zbudujmy przykładową strukturę bazy danych tagów, do której będziemy przypinać różne rzeczy – newsy, artykuły, galerie zdjęć, zdjęcia, cokolwiek.

Najprostsza tabela db_tags o polach:

  • tag_id, UNSIGNED, aby zwiększyć zakres INT – wartości ujemne nie są nam porzebne. Oczywiście primary key oraz auto increment.
  • tag_name, chociażby varchar(255)
  • tag_count, UNSIGNED, INT, ponownie bez znaku, aby zwiększyć zakres, wartości ujemne są nam niepotrzebne. Tutaj będziemy przechowywać liczbę reprezentującą, ile razy użyto tagu do oznaczenia dowolnego zestawu informacji.
CREATE TABLE db_tags (
  tag_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  tag_name VARCHAR(255) NOT NULL ,
  tag_count INT UNSIGNED NOT NULL
) ENGINE = INNODB;

Zastanówmy się, po czym będziemy sortować tagi. Warto założyć klucz na pole tag_count, znacznie przyspieszy późniejsze sortowanie wyników po najpopularniejszych tagach. Jeżeli chcemy sortować po liczbie występowań tagu oraz nazwie (aby chmura była alfabetycznie), warto założyć wspólny klucz na tag_name oraz tag_count. Osobiście sortowanie alfabetyczne zostawiam implementacji klasie tagów dla ksort(), bowiem zapytanie wyciągające tagi jest obarczone limitem, zatem wspólny klucz w bazie danych nie jest mi potrzebny – mniej danych w indeksach.

ALTER TABLE db_tags ADD INDEX (tag_count);

Tworzymy dowolną strukturę danych, która będzie podpinała się do naszych tagów. Pamiętajmy, że do tagów może podpinać się (a przynajmniej powinno, zależy od założeń początkowych projektu) wiele struktur jednocześnie. Wybrałem najbardziej pospolite – newsy w tabeli db_news.

CREATE TABLE db_news (
  news_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  news_title TEXT NOT NULL,
  news_content TEXT NOT NULL
) ENGINE = INNODB;

Pozostało nam stworzyć tabelę wiążącą nasze newsy z tagami (nie tagi z newsami). Tabelę nazwałem db_news_tags. Zawierać ona będzie tylko dwa pola przechowujące identyfikator newsa oraz przypisanego do niego tagu, zachowując typ danych wiążących, czyli INT UNSIGNED. Zakładam wspólny primary key dla obu pól.

  • handler_item – klucz ID newsa,
  • handler_node – klucz ID tagu.
CREATE TABLE db_news_tags (
  handler_item INT UNSIGNED NOT NULL,
  handler_node INT UNSIGNED NOT NULL,
PRIMARY KEY (handler_item, handler_node)
) ENGINE = INNODB;

Buduję relacyjną bazę danych. Gdy jakiś tag zostanie usunięty, bądź gdy jakiś news zostanie usunięty, automatycznie powinien zniknąć wpis z tabeli db_news_tags, zatem używamy kluczy obcych:

ALTER TABLE db_news_tags ADD FOREIGN KEY (handler_item) REFERENCES db_news (news_id) ON DELETE CASCADE;
ALTER TABLE db_news_tags ADD FOREIGN KEY (handler_node) REFERENCES db_tags (tag_id) ON DELETE CASCADE;

Tak zaprojektowaną strukturę danych mogę spokojnie używać do przechowywania danych. Pozostaje kwestia obliczania ilości występowań tagów. Istnieją co najmniej dwie szkoły.

  1. Każda zmiana danych w db_news_handler wywołuje procedurę liczącą tagi. Trzeba mieć na uwadze, że tagi są przeliczane od początku mielenie bazy, ale de facto proces odbywa się po kluczach. Zaletą rozwiązania jest to, że przy bardzo rozbudowanych strukturach (np. liczymy tylko aktywne i widoczne tagi) procedura uwspólnia nam warunki podliczania, używając jej w wielu miejscach nie musimy się martwić o redefiniowanie triggerów.
  2. Dla przedstawionego przykładu w tym poście wystarczy inkrementacja licznika przy dodaniu i dekrementacja przy usunięciu tagu. W większości przypadków właśnie takiego rozwiązania powinno się używać.

Luźny komentarz techniczny (problems, tips & tricks): Aby ominąć problemy wynikłe z założenia w punkcie pierwszym, równie dobrze możemy napisać procedury, które inkrementują/dekrementują liczbę tagów w zależności od warunków (np. tylko wtedy, kiedy tag jest aktywny i widoczny w serwisie). Nikt nie powiedział, że procedury muszą liczyć wszystko od początku możemy się na takie rozwiązanie zgodzić, rezygnujemy natomiast z synchronizacji licznika podczas zmiany warunków, wówczas podczas każdej zmiany warunków, trzeba przekręcić licznik od początku, zliczając wszystkie rekordy wg. ustalonych warunków ręcznie. Triggera należałoby również umieścić w UPDATE (zmiana stanu tagu, np. z niewidocznego na widoczny, z aktywnego na nieaktywny). I to jest najrozsądniejsze rozwiązanie.

W naszym przypadku ograniczymy się do dwóch triggerów, które będą trzymały rękę na pulsie w momencie przypisania tagu do struktury INSERT oraz zerwaniu przypisania DELETE. Zatem:

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: JoShiMa, dodany: 07.01.2011 23:56, tagi: php

Pracując ostatnio nad pewnym projektem przyszło mi zaprogramować niezliczoną ilość formularzy. No taka niestety jest specyfika paneli administracyjnych. Postanowiłam więc przy tej okazji pokazać jak można tworzyć formularze za pomocą modułu formo, jak przekazać formularz do widoku oraz jak w widoku wykorzystać jego elementy. Instalacja modułu formo Moduł formo to dość wygodne narzędzie choć ma pewne niedoróbki. [...]

Autor wpisu: matipl, dodany: 07.01.2011 08:09, tagi: php

php-logo30 grudnia 2010 świat obiegła wiadomość, że PHP jest podatny na dość trywialny błąd. Podczas konwersji stringa do liczby double wartości 2.2250738585072011e-308 przy pomocy rejestrów x87 FPU nasz PHP był w stanie powiesić całą maszynę.

Mimo wszystko dla większości osób ten PHP Remote Exploit nie był zbytnio straszny. Wystarczy, że nasza aplikacja zaopatrzona jest w walidatory formularzy (np. Zend Framework lub Symfony) albo PHP operuje na systemie 64 bitowym, którego błąd nie dotyczył.

Wczoraj, 6 stycznia społeczność PHP, który jest open source (jeśli nie pamiętasz) załatała błąd wydając tym samym wersję PHP 5.3.5 oraz 5.2.17. Dzięki. Zachęcam do aktualizacji albo kompilacji :)

Źródło: PHP 5.3.5 and 5.2.17 Released!

Autor wpisu: batman, dodany: 07.01.2011 08:00, tagi: php

Większość programistów PHP zapytana o SPL albo nie wiedziałaby o co chodzi, albo kojarzyłaby “jakieś klasy do listowania katalogów”. A szkoda, ponieważ SPL oferuje programistom struktury, często wynajdywane przez nich na nowo. Do szerokiego wachlarza możliwości dostępnych w ramach SPL można zaliczyć zestaw klas wyjątków, klasy iteratorów, struktury danych, funkcje autoloadera oraz zestaw interfejsów. W ramach popularyzowania SPL będę opisywał kolejne zagadnienia z nim związane. Dzisiaj poznamy możliwości oferowane przez klasy służące do obsługi plików. Będą to SplFileInfo, SplFileObject oraz SplTempFIleObject.

SplFileInfo

Klasa SplFileInfo stanowi obiektowy interfejs dostępu do informacji o pliku i wygląda nastęująco

SplFileInfo
{
	/* Methods */
	__construct ( string $file_name )
	public int getATime ( void )
	public string getBasename ([ string $suffix ] )
	public int getCTime ( void )
	public SplFileInfo getFileInfo ([ string $class_name ] )
	public string getFilename ( void )
	public int getGroup ( void )
	public int getInode ( void )
	public string getLinkTarget ( void )
	public int getMTime ( void )
	public int getOwner ( void )
	public string getPath ( void )
	public SplFileInfo getPathInfo ([ string $class_name ] )
	public string getPathname ( void )
	public int getPerms ( void )
	public string getRealPath ( void )
	public int getSize ( void )
	public string getType ( void )
	public bool isDir ( void )
	public bool isExecutable ( void )
	public bool isFile ( void )
	public bool isLink ( void )
	public bool isReadable ( void )
	public bool isWritable ( void )
	public SplFileObject openFile ([ string $open_mode = r [, bool $use_include_path = false [, resource $context = NULL ]]] )
	public void setFileClass ([ string $class_name ] )
	public void setInfoClass ([ string $class_name ] )
	public void __toString ( void )
}

Duża ilość metod przekłada się na praktyczne zastosowania klasy, w zupełności wystarczające do uzyskania wszystkich informacji o pliku.

Do czego jest nam potrzebna ta klasa? Przede wszystkim zamyka w jednym pojemniku wszystkie metody zwracające informacje o pliku. Dzięki temu stanowi doskonałą bazę dla wszystkich klas operujących na plikach i katalogach – tak, przekazanie do SplFileInfo nazwy katalogu też zadziała.

SplFileObject

Klasa SplFileObject dziedziczy bezpośrednio po SplFileInfo i uzupełnia ją o metody manipulujące plikiem. Ponadto implementuje interfejsy RecursiveIterator, Traversable, Iterator, SeekableIterator, które dodatkowo zwiększają jej możliwości. O interfejsach napiszę w jednym z kolejnych wpisów. Na teraz wystarczy wiedzieć, że dzięki tym interfejsom można iterować po obiekcie SplFileObject np. pętlą foreach i w każdej iteracji mieć dostęp do kolejnego wiersza z pliku oraz uzyskiwać dostęp do dowolnego miejsca w pliku.

Klasa składa się z następujących metod (plus te, które dziedziczy po SplFileInfo)

SplFileObject extends SplFileInfo implements RecursiveIterator , Traversable , Iterator , SeekableIterator
{
    /* Constants */
    const integer SplFileObject::DROP_NEW_LINE = 1 ;
    const integer SplFileObject::READ_AHEAD = 2 ;
    const integer SplFileObject::SKIP_EMPTY = 6 ;
    const integer SplFileObject::READ_CSV = 8 ;
    /* Methods */
    __construct ( string $filename [, string $open_mode = "r" [, bool $use_include_path = false [, resource $context ]]] )
    public string|array current ( void )
    public boolean eof ( void )
    public boolean fflush ( void )
    public string fgetc ( void )
    public array fgetcsv ([ string $delimiter = "," [, string $enclosure = "\"" [, string $escape = "\\" ]]] )
    public string fgets ( void )
    public string fgetss ([ string $allowable_tags ] )
    public bool flock ( int $operation [, int &$wouldblock ] )
    public int fpassthru ( void )
    public mixed fscanf ( string $format [, mixed &$... ] )
    public int fseek ( int $offset [, int $whence = SEEK_SET ] )
    public array fstat ( void )
    public int ftell ( void )
    public bool ftruncate ( int $size )
    public int fwrite ( string $str [, int $length ] )
    public void getChildren ( void )
    public array getCsvControl ( void )
    public int getFlags ( void )
    public int getMaxLineLen ( void )
    public bool hasChildren ( void )
    public int key ( void )
    public void next ( void )
    public void rewind ( void )
    public void seek ( int $line_pos )
    public void setCsvControl ([ string $delimiter = "," [, string $enclosure = "\"" [, string $escape = "\\" ]]] )
    public void setFlags ( int $flags )
    public void setMaxLineLen ( int $max_len )
    public bool valid ( void )
}

Jeśli się dokładnie przyjrzycie, zauważycie iż większość metod pokrywa się ze znanymi funkcjami, takimi jak fwrite, fgets, eof, fseek, itd. Stosowanie tych metod niczym się nie różni od ich odpowiedników poza tym, że są wywoływane w kontekście obiektu i nie wymagają ciągłego przekazywania uchwytu do pliku.

SplTempFileObject

Ostatnią klasą z grupy obsługi plików jest SplTempFileObject, która umożliwia tworzenie tymczasowych plików w pamięci lub w katalogu tymczasowym. Klasa dziedziczy po SplFileObject i jedyną metodą jaką zawiera jest konstruktor.

SplTempFileObject extends SplFileObject
{
    /* Methods */
    __construct ([ integer $max_memory ] )
}

 

Praktyczne zastosowanie

Głównym celem opisanych powyżej klas jest dostarczenie obiektowego interfejsu do obsługi plików. Wraz z obiektowością dostajemy wyjątki, które są używane zamiast typowych dla standardowych funkcji warningów. Nic nie stoi na przeszkodzie aby rozszerzać funkcjonalność tych klas i dodać np. możliwość serializacji SplFileObject.

Ponieważ dobry przykład wart jest więcej niż tysiąc słów, przejdę bez dalszej zwłoki do konkretnych przypadków użycia.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: batman, dodany: 05.01.2011 08:00, tagi: php

Najnowsza wersja PHP oznaczona numerem 5.3 wprowadziła do języka szereg usprawnień, o których programiści jeszcze niedawno mogli jedynie marzyć. Jednym z takich usprawnień są funkcje anonimowe, nazywane również domknięciami (closures). Mimo swoich wad znacząco wpływają na sposób programowania, wprowadzając do słownika programisty PHP konstrukcje określane mianem funkcji zwrotnych.

Najprostsza funkcja anonimowa ma postać

$funkcja = function()
{
	echo 'Jestem funkcja anonimowa';
};

$funkcja();

Wprawdzie zastosowanie takiej funkcji jest żadne, niemniej powyższy kod daje pogląd na to jak funkcja anonimowa jest definiowana.

Co tak naprawdę powyższy kod robi? Korzysta z wbudowanej w PHP klasy Closure (oznaczonej jako finalna), która zawiera magiczną metodę __invoke. Innymi słowy funkcja anonimowa jest obiektem klasy Closure, co doskonale widać na poniższym przykładzie.

// zmienna $funkcja została zdefinowana w poprzednim przykładzie
var_dump($funkcja);
/*
wynik działa funkcji var_dump:

object(Closure)#1 (0) {
}
*/

Metoda __invoke znajdująca się w klasie Closure, wywoływana jest w momencie, gdy do obiektu odwołujemy się tak, jakby był funkcją. I to jest cały sekret funkcji anonimowych. Tak naprawdę w tle dzieje się coś takiego

$funkcja->__invoke();

Do anonimowych funkcji można przekazywać argumenty w taki sam sposób, jak do standardowych funkcji.

$funkcja = function($arg1, $arg2)
{
	echo $arg1;
	echo $arg2;
};

$funkcja(123, 'abc');

Innym sposobem przekazania danych do funkcji anonimowej jest skorzystanie z zmiennych dostępnych w tym samym zasięgu, w którym znajduje się funkcja.

$zmienna = 123;
$funkcja = function()
{
	echo $zmienna;
};

$funkcja();

Niestety (a może i stety) funkcja anonimowa nie ma do nich bezpośredniego dostępu i to my musimy wskazać jakich zmiennych chcemy użyć. Z tego właśnie względu powyższy kod nic nie wyświetli oraz zgłosi notice.

$zmienna = 123;
$funkcja = function() use ($zmienna)
{
	echo $zmienna;
};

$funkcja();

Dopiero użycie use spowodowało, że zmienna jest widoczna wewnątrz funkcji anonimowej. W celu przekazania większej ilości zmiennych wystarczy oddzielić je od siebie przecinkiem.

W tym momencie ujawniają się dwie poważne wady funkcji anonimowych. Po pierwsze, do funkcji przekazywana jest kopia zmiennej, wykonana w momencie definiowania funkcji, a nie w momencie jest użycia.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Kamil, dodany: 04.01.2011 01:29, tagi: php

Jeszcze kilka dni temu pisałem o atrybucie httpOnly dla ciasteczek, dzięki któremu częściowo (minimalnie) możemy się zabezpieczyć przed atakami typu XSS, czyli przed wstrzykiwaniem niebezpiecznego kodu do naszej strony. Dzisiaj uderzymy w temat z nieco innej strony, mowa bowiem o filtrowaniu danych wchodzących do aplikacji przy pomocy świetniej biblioteki, HTML Purifier. Czym jest HTML Purifier? [...]

Autor wpisu: batman, dodany: 03.01.2011 08:00, tagi: php

Tabele w Windows Azure są trzecim i ostatnim sposobem przechowywania danych w Azure Storage. Pozostałe dwa (bloby oraz kolejki) poznaliśmy w poprzednich dwóch częściach serii. Wbrew pozorom tabele nie są bazą danych. Cechuje ją nierelacyjna struktura służąca do przechowywania danych w postaci encji, gdzie każda z encji może składać się z innych właściwości. Przez encję należy rozumieć pojedynczy wiersz w tabeli, której reprezentacją w kodzie jest pojedyncza klasa złożona z właściwości.

Definiowanie encji

W przypadku PHP encję możemy zdefiniować na dwa sposoby – z określonym schematem oraz ze schematem dynamicznym. Jedyną różnicą w obu podejściach jest to, że encja z określonym schematem wymaga utworzenia klasy będącej jej reprezentacją. Schemat dynamiczny pozwala tworzyć encje w locie.

Encja z określonym schematem

Jak już wspomniałem, ten typ encji wymaga utworzenia klasy. Klasa ta musi dziedziczyć po Microsoft_WindowsAzure_Storage_TableEntity i składać się z właściwości odpowiadających poszczególnym informacjom na temat opisywanego obiektu. Każda właściwość, która ma być przechowywana w Azure Storage, musi być publiczna i poprawnie oznaczona przy pomocy komentarza w stylu PHPDoc. Zamiast właściwości można zastosować publiczne setery i getery, które również wymagają odpowiedniego komentarza.

Przykładowa encja będzie wyglądać nastęująco

class Encja extends Microsoft_WindowsAzure_Storage_TableEntity
{
    /**
     * @azure Imie
     */
    public $imie;

    /**
     * @azure Nazwisko
     */
    public $nazwisko;

    /**
     * @azure Wiek Edm.Int32
     */
    public $wiek;
}

Komentarz opisujący każdą właściwość musi zawierać tag @azure, po którym znajduje się nazwa właściwości (nie musi być taka sama jak nazwa zmiennej). Opcjonalną informacją zawartą w komentarzu jest typ danych. Do dyspozycji mamy:

  • Edm.Binary – dane binarne w postaci tablicy o maksymalnym rozmiarze 64KB
  • Edm.Boolean – wartość logincza
  • Edm.DateTime – data w formacie UTC
  • Edm.Double – wartość zmiennoprzecinkowa
  • Edm.Guid – unikatowy identyfikator
  • Edm.Int32 – liczba całkowita 32 bitowa
  • Edm.Int64 – liczba całkowita 64 bitowa
  • Edm.String – ciąg znaków

Jeśli nie zdefiniujemy żadnego typu, dane zostaną zapisane jako Edm.String.

Encja z dynamicznym schematem

Ten rodzaj encji nie wymaga definiowania klasy encji. Podczas korzystania z tego rodzaju encji wystarczy utworzyć obiekt typu Microsoft_WindowsAzure_Storage_DynamicTableEntity, który dziedziczy po klasie Microsoft_WindowsAzure_Storage_TableEntity, dzięki czemu wszystkie funkcjonalności encji są zachowane. Korzystanie z takiego obiektu jest identyczne jak w przypadku encji o określonym schemacie z tą różnicą, że typ danych definiuje się przy pomocy metody setAzurePropertyType.

$encja = new Microsoft_WindowsAzure_Storage_DynamicTableEntity();
$encja->Imie = 'Jan';
$encja->Nazwisko = 'Kowalski';
$encja->Wiek = 30;
$encja->setAzurePropertyType('Wiek', 'Edm.Int64');
Wbudowane właściwości encji

Oprócz właściwości opisujących dany obiekt, encje zawierają cztery dodatkowe:

  • PartitionKey – klucz partycji określający partycję, w której znajduje się encja
  • RowKey – klucz wiersza, który musi być unikatowy w obrębie partycji
  • Timestamp – określa datę ostatniej zmiany encji
  • Etag – znacznik określający wersję encji

Najważniejszymi są PartitionKey oraz RowKey, stanowiące klucz główny tabeli. Kombinacja tych dwóch właściwości musi być unikatowa.

Korzystanie z tabel i encji

Operacje na tabelach nie różnią się niczym od operacji na blobach i kolejkach. Podobnie jak w przypadku pozostałych mechanizmów przechowywania danych, wszystko sprowadza się do utworzenia pojemnika (w tym przypadku tabeli) i zapisania w nim danych (encji). Tabele dodały do tego schematu możliwość korzystania z transakcji oraz mechanizm filtrowania danych.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.