PHP forráskódok tömörítése
A PHP Tokenizer kiterjesztésről, majd annak kód-zagyváláshoz kapcsolódó felhasználásáról íródott cikkeimet szerencsére nem kis érdeklődés kísérte. Újabb csoportos foglalkozásunkon ezúttal szkriptjeink megrövidítése érdekében vetünk be hatékony terápiás módszereket. Fontos követeleményünk marad továbbra is, hogy kódunk működésében, funkcionalitásában károsodást ne szenvedjen. Várakozásom szerint nem lep meg senkit, hogy ismét a Tokenizer kiterjesztést húzzuk elő varázskalapunkból.
Szerény véleményem szerint a programozók többsége úgy írja forráskódjait, hogy azokat sorokra tördeli, behúzásokkal formázza, nem pedig egy szép szál sorba gépel be minden utasítást. Aki mégis a takarékosság ezutóbbi útján jár, az itt hagyja abba az olvasást.
Ezek a szóközök, tabulátorok és újsor karakterek jelentősen megkönnyítik a forráskód kezelését, bár végső soron csak az állományok méretét növelik. Bár egy ember számára valószínűleg sokkal olvashatóbb a program, ha nem egy sorban vannak az utasítások, egy átlagos PHP értelmező számára nem jelentenek semmi különöset, csak a különböző tokeneket választják el. A rengeteg megjegyzéssel és üres sorokkal tűzdelt PHP állományok nagyobbak, többet foglalnak a memóriából, és tovább tart beolvasni illetve feldolgozni azokat. Nem véletlen, hogy a PHP 5-ben újdonságkent megjelenik a
Nem szabad azonban teljesen fellelkesülni a térközök elmozdításával. Vannak ugyanis olyan tokenek, melyek nem állhatnak egymás mellett. Ilyen például a változó (T_VARIABLE) és az "as" (T_AS) token:Ez a kód-részlet hibát fog eredményezni, mert a bizonyos "as" elemet a feldolgozó az azt megelőző változó (T_VARIABLE token) részeként ismeri fel; ezért kell egy elválasztó token a kettő közé, amely lehet akár egy (vagy több) megjegyzés, szóköz, újsor, stb.
A sok térközön felül a megjegyzéseket is nyugodtan ki lehet gyomlálni, azok is csak a helyet foglalják: sokszor csak dokumentációt tartalmaznak, pedig azt szerintem jobb lenne egész máshol tartani (például egy Documentation.html állományban).
A PHP-ban létezik egy érdekes és meglehetősen hosszú nyitó/záró tag, név szerint aösszehasonlítva a következő kód csak feldolgozási hibát ad:Gondolhatunk még az utolsó pontosvesszőkre és záró elemekre is. Ha PHP programjaink végén elmarad ezek valamelyike, az egyáltalán nem zavarja az értelmezőt. Arra viszont figyelnünk kell, hogy csak az egyiket lehet elhagyni. A következő kód csak hibát ad:az alábbi kettő viszont teljesen korrekt:Ezekből az következik, hogy a PHP kód végén elhelyezett pontosvessző és záró PHP elem párosból egy pontosvesszőnyit érdemes meghagyni, az utolsó záró elemet pedig pontosvesszőként praktikus tovább éltetni.
Létezik két nagyon hasonló konstrukció a PHP-ben azA második megoldás nem csak rövidebb, hanem gyorsabb is, nem fogyaszt annyi memóriát. Míg az elsőnél a Zend motor megkezd egy else ágat és abba még beletesz egy if-et majd még egy else-t, addig a második megoldásnál csak az első elseif-nél hoz létre egy elseif-listát és ezután ebbe kerülnek bele a további feltételek.
A forráskódra közelebbi figyelemmel lévők számára kiderül, hogy bizonyos fura esetekben is meghagyja a T_WHITESPACE tokeneket a függvény:Ilyen esetekben azonban már az eredeti forráskód sem értelmes PHP, ezért már úgysem tudjuk tovább rontani...
Ez az egyszerűen használható eszköz segíthet abban az esetben, ha nem tudunk kód gyosítót telepíteni szerverünkre, de az állomány rendszer terhelése már gondot okoz webhelyünk magas látogatottsága miatt. Ezúttal sem egy komplett obfuszkátor megvalósítása volt a cél, bár az eredményül adódó kódok jelentősen nehezebben olvashatóak.
Mára ennyit, a forráskód használatához és megértéséhez sok sikert!
■ A sorozatban megjelent
- A PHP Tokenizer kiterjesztése
- PHP obfuszkátor
- PHP forráskódok tömörítése
Mitől akarunk megszabadulni?
Szerény véleményem szerint a programozók többsége úgy írja forráskódjait, hogy azokat sorokra tördeli, behúzásokkal formázza, nem pedig egy szép szál sorba gépel be minden utasítást. Aki mégis a takarékosság ezutóbbi útján jár, az itt hagyja abba az olvasást.
Ezek a szóközök, tabulátorok és újsor karakterek jelentősen megkönnyítik a forráskód kezelését, bár végső soron csak az állományok méretét növelik. Bár egy ember számára valószínűleg sokkal olvashatóbb a program, ha nem egy sorban vannak az utasítások, egy átlagos PHP értelmező számára nem jelentenek semmi különöset, csak a különböző tokeneket választják el. A rengeteg megjegyzéssel és üres sorokkal tűzdelt PHP állományok nagyobbak, többet foglalnak a memóriából, és tovább tart beolvasni illetve feldolgozni azokat. Nem véletlen, hogy a PHP 5-ben újdonságkent megjelenik a
php_strip_whitespace()
függvény, mely hasonló célokat szolgál, mint az itt bemutatott megoldás.Nem szabad azonban teljesen fellelkesülni a térközök elmozdításával. Vannak ugyanis olyan tokenek, melyek nem állhatnak egymás mellett. Ilyen például a változó (T_VARIABLE) és az "as" (T_AS) token:
<?php foreach($arrayas$key=>$value){ ?>
A sok térközön felül a megjegyzéseket is nyugodtan ki lehet gyomlálni, azok is csak a helyet foglalják: sokszor csak dokumentációt tartalmaznak, pedig azt szerintem jobb lenne egész máshol tartani (például egy Documentation.html állományban).
A PHP-ban létezik egy érdekes és meglehetősen hosszú nyitó/záró tag, név szerint a
<script language="php"></script>
páros. Ezekből érdemes <?
-t és ?>
-t csinálni, ugyanúgy mint az összes többi (<?php <%
) nyitó és záró elemből. Mivel most a méret a lényeg, ezért nem mindegy az a néhány karakter sem, amit így nyerünk. További előnye a <?
rövid nyitó elem használatának, hogy nem kell utána szóközt, újsort vagy tabulátort írni, hanem egyből jöhet például egy értékadó utasítás:<?$változóm='értéke'?>
<?php$változóm='értéke'?>
<?exit
<?exit;
<?exit?>
Létezik két nagyon hasonló konstrukció a PHP-ben az
else if
és az elseif
. Bár ezek lényegében ugyanazt a logikai műveletet eredményezik, belsőleg nem ugyanazt a kódot generálják. Lássuk a következő két példát:
<?php
if(1);
else if(0);
else if(1);
else;
if(1);
elseif(0);
elseif(1);
else;
?>
Lássuk a medvét
A következőkben bemutatnám azt a kódot, ami korábbi tokenizer ismereteinkre építve elvégzi a kirótt feladatot. A logikát egy függvényben valósítjuk meg, amely aphp_strip_whitespace()
fantázianevet kapta, a fent említett okok miatt. Azt fontosnak tartom megjegyezni, hogy a PHP 5-ös azonos nevű új függvénye csak egy szóközt csinál a T_WHITESPACE tokenekből és törli a megjegyzéseket, mi pedig ennél többet vállaltunk. Függvényünk egy paramétert vár, mely karaktersorozatként átadott PHP kódot tartalmaz, és ennek a rövidített változatával tér vissza.
<?php
function php_strip_whitespace($code){
$tokens = token_get_all($code); // kérjük a $code PHP tokenjeit
$return = ''; // visszatérési érték
while(list($i, $token) = each($tokens)) // végig kell menni az összes tokenen
if(is_string($token)) // ez a token egy sztring, nincs mit tenni
$return .= $token;
else switch($token[0]){ // $token[0] tartalmazza a token típusát
case T_OPEN_TAG: // nyitó tag, bármi is legyen az, inkább váljon <?-vé
$return .= '<?';
break;
case T_CLOSE_TAG: // záró tag
if(!@$tokens[$i + 1]){ // ha az utolsó, akkor nem kell kitenni, de:
if(@$tokens[$i - 1] !== ';' // ha előtte nem volt pontosvessző,
&& !@($tokens[$i - 1][0] == T_WHITESPACE
&& $tokens[$i - 2] === ';'))
$return .= ';'; // akkor mostmár legyen
}else
$return .= '?>'; // mindenféle záró tagek is inkább ilyenek legyenek
break;
case T_COMMENT: // ilyenkor egy megjegyzéssel
case T_ML_COMMENT: // vagy töbsoros megjegyzéssel van dolgunk, ami
case T_WHITESPACE: // üresség, és nem biztos, hogy kívánatos, ezért
// jöjjön egy szép feltétel-lista,
// mely eldönti, hogy kell-e oda egy "elválasztó"
if(@($tokens[$i - 1][0] == T_CLASS // class, class Osztály
|| $tokens[$i - 1][0] == T_EXTENDS
|| $tokens[$i + 1][0] == T_EXTENDS // extends, class Osztály extends Másikosztály
|| $tokens[$i - 1][0] == T_FUNCTION // function, function Függvény
|| (($tokens[$i - 1][0] == T_CASE // ha előtte egy case, echo, print, return áll, akkor
|| $tokens[$i - 1][0] == T_ECHO // meg kell nézni mi is jön utána...
|| $tokens[$i - 1][0] == T_PRINT
|| $tokens[$i - 1][0] == T_RETURN
|| ($tokens[$i - 1][0] == T_ELSE // else, de ugye utána nem if jön...
&& $tokens[$i + 1][0] != T_IF)) // mert így az else és az if egybeíródik és lesz belőle elseif
&& ($tokens[$i + 1][0] == T_ARRAY // array(), return array(...)
|| $tokens[$i + 1][0] == T_BREAK // break, else break
|| $tokens[$i + 1][0] == T_CLASS_C // __CLASS__, echo __CLASS__
|| $tokens[$i + 1][0] == T_CONTINUE // continue, else continue
|| $tokens[$i + 1][0] == T_DECLARE // declare, else declare
|| $tokens[$i + 1][0] == T_DNUMBER // törtszám, case 1.23: case .5:
|| $tokens[$i + 1][0] == T_DO // do ciklus, else do{...}while(...);
|| $tokens[$i + 1][0] == T_ECHO // echo, else echo
|| $tokens[$i + 1][0] == T_EMPTY // empty(), return empty(...)
|| $tokens[$i + 1][0] == T_EXIT // exit, else exit;
|| $tokens[$i + 1][0] == T_EVAL // eval(), case eval()
|| $tokens[$i + 1][0] == T_FILE // __FILE__, return __FILE__
|| $tokens[$i + 1][0] == T_FOR // for ciklus, else for(...);
|| $tokens[$i + 1][0] == T_FOREACH // foreach ciklus, else foreach(...);
|| $tokens[$i + 1][0] == T_FUNC_C // __FUNCTION__, echo __FUNCTION__
|| $tokens[$i + 1][0] == T_GLOBAL // global, else global $...
|| $tokens[$i + 1][0] == T_INCLUDE // include, return include ...
|| $tokens[$i + 1][0] == T_INCLUDE_ONCE // include_once, echo include_once(...)
|| $tokens[$i + 1][0] == T_ISSET // isset, return isset(...);
|| $tokens[$i + 1][0] == T_LINE // __LINE__, echo __LINE__
|| $tokens[$i + 1][0] == T_LIST // list(), else list(...)
|| $tokens[$i + 1][0] == T_LNUMBER // egész szám, echo 0xff; echo 123
|| $tokens[$i + 1][0] == T_PRINT // print, else print ...
|| $tokens[$i + 1][0] == T_REQUIRE // require
|| $tokens[$i + 1][0] == T_REQUIRE_ONCE // require_once
|| $tokens[$i + 1][0] == T_RETURN // return, else return ...
|| $tokens[$i + 1][0] == T_STATIC // static, else static $...
|| $tokens[$i + 1][0] == T_STRING // állandó vagy függvénynév, echo phpversion(); echo PHP_VERSION
|| $tokens[$i + 1][0] == T_SWITCH // switch, else switch(...){}
|| $tokens[$i + 1][0] == T_WHILE)) // while, else while(...);
|| $tokens[$i - 1][0] == T_NEW // new, new Osztály();
|| ($tokens[$i + 1][0] == T_AS // utána 'as' előtte változó, foreach($tömb as ...)
&& $tokens[$i - 1][0] == T_VARIABLE) // míg ilyenkor nem kell: foreach(array()as ...)
|| (($tokens[$i + 1][0] == T_LOGICAL_AND // logikai műveletek (AND, OR és XOR), azt kell vizsgálni, nincs-e
|| $tokens[$i + 1][0] == T_LOGICAL_OR // előttük olyasmi, amivel esetleg "összekeverhető"
|| $tokens[$i + 1][0] == T_LOGICAL_XOR)
&& ($tokens[$i - 1][0] == T_VARIABLE // változó, 1 XOR $változó
|| $tokens[$i - 1][0] == T_STRING // állandó, 3.14 AND M_PI
|| $tokens[$i - 1][0] == T_FILE
|| $tokens[$i - 1][0] == T_LINE // __LINE__, __LINE__ AND 1
|| $tokens[$i - 1][0] == T_FUNC_C
|| $tokens[$i - 1][0] == T_CLASS_C))
|| (($tokens[$i - 1][0] == T_LOGICAL_AND // megint a logikai műveletek, de most utánuk kell nézni
|| $tokens[$i - 1][0] == T_LOGICAL_OR
|| $tokens[$i - 1][0] == T_LOGICAL_XOR)
&& ($tokens[$i + 1][0] == T_STRING // állandó vagy függvény, OR isset
|| $tokens[$i + 1][0] == T_FILE
|| $tokens[$i + 1][0] == T_LINE
|| $tokens[$i + 1][0] == T_FUNC_C
|| $tokens[$i + 1][0] == T_CLASS_C
|| $tokens[$i + 1][0] == T_EXIT // exit vagy die, ... OR die(...)
|| $tokens[$i + 1][0] == T_EVAL
|| $tokens[$i + 1][0] == T_LIST // list, AND list(...) =
|| $tokens[$i + 1][0] == T_LNUMBER
|| $tokens[$i + 1][0] == T_DNUMBER))))
$return .= ' '; // ha már annyira kell, akkor
// legyen csak egy szóköz belőle
break;
default: // minden más tokent békén kell hagyni,
$return .= $token[1];
break;
}
return($return); // vissza a feladónak
}
?>
A forráskódra közelebbi figyelemmel lévők számára kiderül, hogy bizonyos fura esetekben is meghagyja a T_WHITESPACE tokeneket a függvény:
<?php
case array():
return exit;
echo echo;
?>
Ez az egyszerűen használható eszköz segíthet abban az esetben, ha nem tudunk kód gyosítót telepíteni szerverünkre, de az állomány rendszer terhelése már gondot okoz webhelyünk magas látogatottsága miatt. Ezúttal sem egy komplett obfuszkátor megvalósítása volt a cél, bár az eredményül adódó kódok jelentősen nehezebben olvashatóak.
Mára ennyit, a forráskód használatához és megértéséhez sok sikert!
Teszteredmenyek? ;) bye, B
bye, Benjamin
tesztel és
(talán) még a drupalt "tömörítve" is érezhető valami, mivel - ahogy láttam - egy-egy modul mindenféle dolga egy filébe van sűrítve,
eredmények...
az egészről végül generál egy táblázatocskát, ím néhány példa:
ADODB 4.23
Drupal 4.4.1
phpBB 2.0.8a
PHPNuke 7.2
eredmények?
Re: KiB?
KiB = KB (azaz KiloByte)
Kicsit pontosabban...
Adott a "kilo" előtag, amit ugye egy kilogramm, kilométer és stb. kontextusban 1000-nek értünk. Azaz egy kilogramm 1000 gramm és ugyanez a kilométerre is.
Ha ugyanezt a kilo-t a byte elé tesszük, az lenne és az a logikus, hogy szintúgy 1000-et értsünk alatta, azaz 1 kB = 1000 byte. Ennek ellenére a rossz értelmezése terjedt el a dolognak, és 1 kB != 1024 byte-ot szoktak érteni alatta.
Erre lett azonban bevezetve a hülyén hangzó kibi nevű előtag, ami a bináris "kilo"-nak megfelelő szorzót, azaz az 1024-et jelzi. 1 KiB = 1024 byte.
A bevezetője ennek az oldalnak hasznos olvasmány, illetve a táblázatokból az is megtudható, hogy mi a helyes írásmódja a mértékegységeknek, mikor kell kis B-t, kis K-t, mikor kell nagy B-t, nagy K-t írnunk.
-boogie-
WOW :)
mindenesetre jogos, jelen példában a KiB kibibyte-ot jelentett :)
nuke dög
par apro bug..
A fentivel a kovetkezo gondok vannak:
a foreach($this->attribute as $key => $value) tipusu ciklusokat elrontja, valamint a return new Akarmi() tipusuakbol is returnnew-t csinal..
Egyiknel az i-1. token tipusat kell ellenorizni T_RETURN-re, mig utobbi esetben az as elotti T_OBJECT_OPERATOR-t kell figyelni.
Amugy jo, feleakkora lett a kodom (es szazadannyira atlathato :D)
Bar mondjuk kimondott gyorsulast sem tapasztaltam..
De meg tesztelem :-) Minden tizedmasodperc szamit! :-)
rovarírtás
a javított változatban amúgy nem a T_OBJECT_OPERATOR van figyelve, hanem inkább a T_STRING: a T_OBJECT_OPERATOR és a T_AS közöt nem mindig csak egy token foglal helyet
forech(${'változó'} as $kulcs => $érték);
?>
bbalint
in_array()?
hmü
számomra így olvashatóbb, de ha nagyon lassú akód, akkor a bácsi megcsinálhatja in_array()olgatással, statikus változók felvételével.
amúgy, így jó?
:)
Vicczen kívül teccik ez is, mint a többi cikked a tokenizerrel...
BYE: TeeCee :o)
<?php miért?
Tudom ha jól átrágnám megtalálhatnám magam is a hiba okát de most nincs sok időm. Ha esetleg...
Amúgy már nagyon régen szerettem volna egy ilyen kis gyönyörűséget, úgy hogy a cikk COOL!
Belenéztem egy kicsit...
<?php
if( ...
Tehát ha egy if utasítás következik akkor sajna nincs space.
teszt
bbalint
ööö
szóval, a PHP-ban úgy van, hogy a
<?php
T_OPEN_TAG nemcsak abból a két karakterből (kisebb mint- és kérdőjel) áll, hanem egy vagy több térköz karakterből (szóköz, tabulátor, lapdobás, újsor- és kocsivissza-) karakterből is. talán ebből is eredhet a hiba, node "mi lenne ha?" a problémás file első [hibásan "tömörített"] sorát beidéznéd...én is boldogabb volnék,
bbalint