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-