A PHP Tokenizer kiterjesztése
A PHP fejlődése során egyre több belső lehetőséget tett elérhetővé a nyelvet használó programozók számára, gondolhatunk itt például a
Cikkemben a PHP Tokenizer kiterjesztéséről szeretnék néhány jó szót szólni, mely a PHP belső világát mutatja meg, a nyelvi feldolgozó használatát teszi lehetővé szkriptjeinkben. Szerintem ez az, amit (talán sokan is) kerestek. Írásom a haladóbbaknak szól, tehát aki nem látta még a PHP forráskódját illetve nem fordított PHP-t, annak lehet, hogy megfekszi a gyomrát, tehát csak óvatosan!
kimenete ez lesz:A
Hozzávalók:
Elöljáróban, az igazítást szóközökkel, míg az
Először is, kell egy ciklus, amivel végigmegyünk a kapott tokeneken:Tökéletesen megfelel a célnak, sőt ha kell megnézhetjük az előző, következő, tegnapi, stb. tokent is egy
Most jön a feladat érdemi része, csinálni is kell valamit a tokenekkel. Alapvetően kétféle token van: Az egyik egyszerű karaktersorozat, valójában csak egy karakterből áll. Ezekkel találkozhatunk ilyenkor:A másik bonyolultabb fajta, mely tömb. A tömb első eleme a token numerikus értéke. Ebből olvashatóbbat a
E rövid bevezető után lássuk a kódot (megjegyzésekkel segítve a megértését):S ezzel meg is vagyunk egy PHP-kód szépítőszerrel, jó étvágyat!
■ parse_ini_file()
függvényre is, mely a php.ini feldolgozó kódot teszi a programozók számára is felhasználhatóvá.Cikkemben a PHP Tokenizer kiterjesztéséről szeretnék néhány jó szót szólni, mely a PHP belső világát mutatja meg, a nyelvi feldolgozó használatát teszi lehetővé szkriptjeinkben. Szerintem ez az, amit (talán sokan is) kerestek. Írásom a haladóbbaknak szól, tehát aki nem látta még a PHP forráskódját illetve nem fordított PHP-t, annak lehet, hogy megfekszi a gyomrát, tehát csak óvatosan!
A sorozatban megjelent
- A PHP Tokenizer kiterjesztése
- PHP obfuszkátor
- PHP forráskódok tömörítése
Bevezető
A PHP-ben a 4.2.0-s változat óta létezik eme kiterjesztés, mely "alig" használható valamire. A 4.3.2-es verzióban a forráskódja 20kb és két függvényt definiál, atoken_get_all()
-t és a token_name()
-et, illetve rengeteg (106) állandót. Ezt a kiterjesztést egy portál fejlesztésénél, XML-feldolgozásnál és egyáltalán sehol az életben nem igazán lehet felhasználni. Inkább amolyan programozói eszköz, de még milyen!Tényleg, milyen?
Atoken_get_all()
egy sztringet vár, melyet átad a Zend lexikális elemzőjének, majd annak "kimenetét" visszaadja egy tömbben. Például az alábbi kód:print_r(token_get_all('<?php echo "Hello Weblabor!"; ?>'));
Array
(
[0] => Array
(
[0] => 354
[1] => < ?php
)
[1] => Array
(
[0] => 314
[1] => echo
)
[2] => Array
(
[0] => 357
[1] =>
)
[3] => Array
(
[0] => 313
[1] => "Hello Weblabor!"
)
[4] => ;
[5] => Array
(
[0] => 357
[1] =>
)
[6] => Array
(
[0] => 356
[1] => ? >
)
)
token_name()
egy számot vár, és amennyiben van olyan értékű T_*
állandó, akkor annak nevét adja vissza, például:<?php
token_name(354) == T_OPEN_TAG
token_name(314) == T_ECHO
?>
Mire jó?
Néhány érdekes lehetőség a tokenizer által nyílt utak közül:- Készíthetünk olyan szkripteket, melyek képesek futtatni más PHP fájlokat.
- Fejleszthetünk a beépített
highlight_string()
éshighlight_file()
helyett saját célra jobban megfelelő megvalósítást:Ezzel a függvénnyel kódszínezett forráskód könnyen testreszabható stíluslappal, pl. megadható, hogy a<?php function __highlight_string($str) { print '<pre>'; $tokens = token_get_all($str); while(list(, $token) = each($tokens)) { if (is_string($token)) { print '<span class="string">' . $token . '</span>'; } else { print '<span class="' . token_name($token[0]) . '">' . $token[1] . '</span>'; } } print '</pre>'; } ?>
alkalmazásával a nyitó.T_OPEN_TAG {color: #f00;}
<?php
,<?
,<%
tagek piros színben pompázzanak.
- Kigyűjthető az összes használt változó/állandó/függvénynév egy fájlból. A változóneveket például így kell összeszedni:Ez a függvény egy tömbben fogja visszaadni a megadott kódban használt változók neveit (dollár-karakterek nélkül), és sokkal pontosabb, gyorsabb, mint egy reguláris kifejezés.
<?php function varnames($str) { $tokens = token_get_all($str); while (list(, $token) = each($tokens)) { if ($token[0] == T_VARIABLE) { $return[substr($token[1], 1)] = TRUE; } } return(array_flip($return)); }?>
- Persze, nemcsak ilyen statisztika jellegű gyűjtögető életmódra idomítható, hanem cseréket is elvégezhet akár:Ez a függvény a paraméterben kapott forráskódot írja ki, de ha abban szerepel valamilyen definiált állandó, akkor azt behelyettesíti annak értékére:
<?php function replace_consts($str) { $tokens = token_get_all($str); while (list($i, $token) = each($tokens)) { if (is_string($token)) { print $token; } else { if ($token[0] == T_STRING && $tokens[$i + 1] !== '(' && defined($token[1])) { var_export(constant($token[1])); } else { print $token[1]; } } } } ?>
eredménye ez lesz:<?php replace_consts('<?php echo M_PI;?>'); ?>
<?php echo 3.1415926535898;?>
- Megvalósíthatók "durvább" dolgok is, mindenkinek a saját fantáziájára bízom!
Hogyan?
Hogyan is kell valamit kezdeni ezzel a kiterjesztéssel? Nos, íme egy receptszerű dolog: "Hogyan csináljunk nagyon egyszerű kód szépítőt (code beautifier) rövid idő alatt?".Hozzávalók:
- PHP, tokenizer kiterjesztéssel;
- programozó;
- egy
$indent
változó melyben az igazítás mértékét tároljuk és kezdeti értéke
nulla (nincs semmi beljebb-kezdés)
- egy
$tokens
tömb, melyben atoken_get_all()
által kapott tokenek vannak
Elöljáróban, az igazítást szóközökkel, míg az
$indent
mennyiségű igazítást az str_repeat(' ', $indent)
függvény értékének kiiratásával oldjuk meg.Először is, kell egy ciklus, amivel végigmegyünk a kapott tokeneken:
<?php
while(list($i, $token) = each($tokens))
?>
$tokens[$i + 1]
hivatkozással.Most jön a feladat érdemi része, csinálni is kell valamit a tokenekkel. Alapvetően kétféle token van: Az egyik egyszerű karaktersorozat, valójában csak egy karakterből áll. Ezekkel találkozhatunk ilyenkor:
; : , . [ ] ( ) | ^ & + - / * = % ! ~$ < > ? @
token_name()
függvénnyel csinálhatunk, de ez most nem szükséges. A tömb második eleme a token sztring értéke, pl. <?php
, while
, list
, ?>
E rövid bevezető után lássuk a kódot (megjegyzésekkel segítve a megértését):
<?php
// Akkor lássuk az egyszerűbb, egykarakteres tokeneket:
if (is_string($token)) {
switch($token) {
// Általában a pontosvesszők és kettőspontok után szokott lenni
// újsor karakter, tegyünk mi is azon melegében és csináljunk igazítást
// is, mivel egy újabb programsorhoz érkeztünk el.
case ';':
case ':':
print $token . chr(10) . str_repeat(' ', $indent);
break;
// A nyitó- és záró kapcsos zárójelek után is szokott lenni újsor, de
// ezeket külön kell kezelni, mivel egy "blokk" (pl. függvény definíció)
// kezdetét/végét jelölik, ezért nemcsak újsor karakter kell, hanem az
// igazítás mértékét is manipulálni kell:
case '{':
print $token . chr(10) . str_repeat(' ', ++$indent);
break;
case '}':
print $token . chr(10) . str_repeat(' ', --$indent);
break;
// Ezzel meg is volnánk, csak a maradék többi karakterről
// kell gondoskodni; azaz csak kiíratni őket és lezárni a
// switch-blokkot:
default:
print $token;
break;
}
}
// Jöjjenek hát a bonyolultabb fajta tokenek, vizsgáljuk csak
// a token fajtáját.
else {
switch($token[0]) {
// Újsort és igazítás teszünk a nyitó PHP tagek elé, ill. hogy a PHP
// kód is beljebb legyen igazítva teszünk igazítást is:
case T_OPEN_TAG:
print '< ?php' . chr(10) . str_repeat(' ', ++$indent);
break;
// Még azt szeretnénk elérni, hogy az értékadások (T_*_EQUAL) és
// értékvizsgálatok (T_*_IDENTICAL) elé és után is tegyünk egy
// szóközt, hogy szép $változó = érték; formátumhoz jussunk
case T_AND_EQUAL:
case T_BOOLEAN_AND:
case T_BOOLEAN_OR:
case T_CONCAT_EQUAL:
case T_DIV_EQUAL:
case T_DOUBLE_ARROW:
case T_IS_EQUAL:
case T_IS_GREATER_OR_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_EQUAL:
case T_IS_NOT_IDENTICAL:
case T_LOGICAL_OR:
case T_LOGICAL_XOR:
case T_LOGICAL_AND:
case T_IS_SMALLER_OR_EQUAL:
case T_MINUS_EQUAL:
case T_OR_EQUAL:
case T_PLUS_EQUAL:
case T_SL:
case T_SL_EQUAL:
case T_SR:
case T_SR_EQUAL:
case T_XOR_EQUAL:
// Itt felsoroltuk az összes értékadás típust a teljesség kedvéért, node
// mit is kell csinálni? Kiírni egy szóközt, a token értékét (legyen az
// >> vagy .=) majd megint egy szóköz, hogy szép legyen
print ' ' . $token[1] . ' ';
break;
// A többi tokennel peddig itt nem foglalkozunk, azok nyugodtan mehetnek
// a kimenetre és zárjuk le a switchet is
default:
print $token[1];
break;
}
}
?>
Irodalom
Nem, nem a felhasznált birodalom, hanem amit érdemes nézegetni:- PHPdoksi: http://hu.php.net/manual/hu/
- Tokenizer extension: http://hu.php.net/manual/hu/ref.tokenizer.php
- Tokenek teljes listája: http://hu.php.net/manual/hu/tokens.php
Google-s link nem jo
Re: Google-s link nem jo
-boogie-
Jó cikk, mindjárt ki is pr
Üdv: laze
valami rossz
htpt://hu.php.net/manual/hu/tokens.php - itt egy apró helyesírási hiba van, a drupal/hozzászólás részében pedig fölösleges utf8_encode(), mivel az előző hozzászólás témája kicsit fura...
ami meg fura, hogy hiányoznak a kódrészletek a cikkből...
(pedig fontos lenne...)
no, jó éjt:
bbalint
Komment címek
Re: valami rossz
Elgépelés javítva, a kódrészletek visszatéve ("kis" bug volt a rendszerben...)
-boogie-