[PHP5] Opbouw van tree versnellen

Pagina: 1
Acties:

  • Apache
  • Registratie: Juli 2000
  • Laatst online: 17-08 14:28

Apache

amateur software devver

Topicstarter
Hey,

K'heb net een template engine geschreven, zoals velen hier, op dit moment is hij zo goed als volledig operationeel, dat is het probleem dan ook niet.

Wanneer ik de tree opbouw die bestaat uit een type en de waarde van de child doet hij hier vrij lang over. Ik maak me nu nog niet meteen zorgen aangezien hij ontwikkeld is voor PHP5 en mogelijk daardoor nog niet zo heel geoptimaliseerd is op sommige punten.

1) De inhoud van een file word aan de functie toTree() gegeven die char per char de tekst afloopt tot hij een START_TOKEN vind dat niet geescaped is. Anders begint hij een nieuwe tag, vind hij een END_TOKEN dan gaat hij controleren wat er in de vorige tag zat.

Code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    private function toTree($templateContent){
    
        $pointer        = 0;
        $openTagCount   = 0;
        $tmpstring      = '';
        $contentLenght  = strlen($templateContent);
        
        while ($pointer < $contentLenght) {
        
            if ($templateContent[$pointer] == START_TOKEN 
                && ((isset($templateContent[$pointer - 1])
                && $templateContent[$pointer - 1] != ESCAPE_CHAR)
                || $pointer == 0)){
                // new tag starts
                if (trim($tmpstring) != ""){
                
                    $tag[TAG_TYPE]  = T_TEXT;
                    $tag[TAG_VALUE] = $tmpstring;
                    array_push($this->tplTree, $tag);
                
                }   
                    
                $tmpstring = '';
                $openTagCount++;
            
            } else if ($openTagCount > 0
                && $templateContent[$pointer] == END_TOKEN 
                && ((isset($templateContent[$pointer - 1])
                && $templateContent[$pointer - 1] != ESCAPE_CHAR)
                || $pointer == 0)){
                // current tag ends
                $tag = $this->determineTagType($tmpstring, $pointer);
                
                if ($tag[TAG_TYPE] != T_DISPOSABLE){
                
                    array_push($this->tplTree, $tag);
                    
                }
                
                $tag[TAG_TYPE]  = T_TEXT;
                $tmpstring = '';
                $openTagCount--;
                
            } else {

                $tmpstring .= $templateContent[$pointer];
                
            }
            
            $pointer++;
        
        }
        
        // push last value on the stack
        array_push($this->tplTree, array(T_TEXT, $tmpstring));
        
        if ($openTagCount > 0){
        
            trigger_error('template::toTree(): Some tags did not have a closing tag.', E_USER_WARNING);
        
        }
        
    }


2) voor elke END_TOKEN zal hij checken wat er in die tag zat en of hij daar nog iets mee moet aanvangen. Dit gebeurt door erg simpele en voor de hand liggende functie's.

Code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    private function determineTagType($tag, $pointer){
        $tagarr[TAG_TYPE]  = T_DISPOSABLE;
        $tagarr[TAG_VALUE] = null;
        
        if (substr($tag, 0, 1) != '/'){
            // start tags
            switch (true){
                case substr($tag, 0, 3) == 'php':
                    $tagarr[TAG_TYPE] = T_PHP;
                    ob_start();
                    eval('print('.substr($tag, 4).');');
                    $tagarr[TAG_VALUE] = ob_get_contents();
                    ob_end_clean();
                    break;
                case substr($tag, 0, 2) == 'if':
                    $tagarr[TAG_TYPE] = T_START_IF;
                    $tagarr[TAG_VALUE] = $this->evaluateIf(substr($tag, 3));
                    break;      
                case substr($tag, 0, 7) == 'else if':
                    $tagarr[TAG_TYPE] = T_ELSE_IF;
                    $tagarr[TAG_VALUE] = $this->evaluateIf(substr($tag, 8));
                    break;
                case substr($tag, 0, 4) == 'else':
                    $tagarr[TAG_TYPE] = T_ELSE;
                    $tagarr[TAG_VALUE] = null;
                    break;
                case substr($tag, 0, 4) == 'file':
                    //$tagarr[TAG_TYPE] = T_FILE;
                    $tagarr[TAG_TYPE] = T_DISPOSABLE; // get rid of this, it will be added to the tree by toTree()
                    $tmp = substr($tag, 5);
                    
                    if (file_exists($this->skinpath . $tmp)){
                    
                        $tagarr[TAG_VALUE] = file_get_contents($this->skinpath . $tmp);
                        $this->toTree($tagarr[TAG_VALUE]);
                        
                    } else {
                    
                        trigger_error('The file: '. $this->skinpath . $tmp .' does not excist on the harddrive.', E_USER_ERROR);
                        
                    }
                    
                    break;
                case substr($tag, 0, 7) == 'varfile':
                    //$tagarr[TAG_TYPE] = T_VARFILE;
                    $tagarr[TAG_TYPE] = T_DISPOSABLE; // get rid of this, it has already been added to the tree
                    $tmp = substr($tag, 8);
                    
                    if (isset($this->tplVarFiles[$tmp])){
                        
                        if (file_exists($this->skinpath . $this->tplVarFiles[$tmp])){
                        
                            $tagarr[TAG_VALUE] = file_get_contents($this->skinpath . $this->tplVarFiles[$tmp]);
                            $this->toTree($tagarr[TAG_VALUE]);
                            
                        } else {
                            
                            trigger_error('template::determineTagType(): The varfile: '. $this->skinpath . $this->tplVarFiles[$tmp] .' does not excist on the harddrive.', E_USER_ERROR);
                            
                        }
                        
                    } else {
                        
                        trigger_error('template::determineTagType(): The varfile: '. $tmp .' does not excist in the varfile list.', E_USER_ERROR);
                        
                    }
                    break;
                case substr($tag, 0, 4) == 'loop':
                    $tagarr[TAG_TYPE] = T_START_LOOP;
                    $tagarr[TAG_VALUE] = $this->evaluateLoop(substr($tag, 5));
                    break;
                case substr($tag, 0, 7) == 'foreach':
                    $tagarr[TAG_TYPE] = T_START_FOREACH;
                    $tagarr[TAG_VALUE] = $this->evaluateForEach(substr($tag, 8));
                    break;                  
                default:
                    if (isset($this->tplLoopVars[$tag])){

                        $tagarr[TAG_TYPE] = T_LOOP_VAR;
                        $tagarr[TAG_VALUE] = '$'.$tag.'['.VAL.']';
                        
                        for ($i = 0; $i < $this->tplLoopVars[$tag][DIMENSIONS]; $i++){
                        
                            $tagarr[TAG_VALUE] .= '[$i['.$i.']]';
                            
                        }
                        
                    } else if (isset($this->tplVars[$tag])){
                    
                        $tagarr[TAG_TYPE] = T_VAR; // no longer a T_VAR it is now regular text
                        $tagarr[TAG_VALUE] = $this->tplVars[$tag];
                    
                    } else {
                        
                        $tagarr[TAG_TYPE] = T_VAR;
                        $tagarr[TAG_VALUE] = START_TOKEN . $tag . END_TOKEN;
                                                
                    }
                    break;                                                                                             
            }

        } else {
            // end tags
            switch (true){
                case ($tag == "/if"):
                    $tagarr[TAG_TYPE] = T_END_IF;
                    break;
                case ($tag == "/foreach"):
                    $tagarr[TAG_TYPE] = T_END_FOREACH;
                    break;      
                case ($tag == "/fe"):
                    $tagarr[TAG_TYPE] = T_END_FOREACH;
                    break;
                case ($tag == "/loop"):
                    $tagarr[TAG_TYPE] = T_END_LOOP;
                    break;
            }           
            
        }

        return $tagarr;
    
    }


Is dit een redelijke aanpak?
zoja, wat zou er nog sterk geoptimaliseerd kunnen worden, aangezien hij over het samenstellen van de tree ong 80% van de hele parsetijd mee bezig is.
zoniet, zijn hier "standaard" oplossingen/constructie's voor, of (electronische)lectuur hierrond?

Aangezien dit nogal aso grote lappen code zijn en die er misschien snel afgeschopt zullen worden door een modje is hier een link naar de 2 fragmenten.

If it ain't broken it doesn't have enough features


  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 13-01 07:19
Deze is ongeveer 20x zo snel in php4.3, ik naam dat dat ook geld voor php5.
Het is niet precies hetzelfde, je moet zelf nog even de tags afhandelen en de boel in functies gooien.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
define(ESCAPE_CHAR,'x');
define(START_TOKEN,'(');
define(END_TOKEN,')');

define(TAG_BAD,0);
define(TAG_IF,1);
define(TAG_ELSE,2);
define(TAG_ELSEIF,3);
define(TAG_LOOP,4);
define(TAG_FILE,5);
define(TAG_PHP,6);
define(TAG_VARFILE,7);
define(TAG_FOREACH,8);
define(TAG_ENDIF,9);
define(TAG_ENDFOREACH,10);
define(TAG_ENDFE,10);
define(TAG_ENDLOOP,11);

$templateContent = '(a)sdakx(asdkj(asdlkx)sad)(if asd)
asdkl (else askdlj)asd';

$tags = array();
$tokens_parsed = 0;
$no_of_tokens = preg_match_all("/(?<!".ESCAPE_CHAR.")(\\".START_TOKEN."|\\".END_TOKEN.")/",$templateContent,$tokens,PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE );
for ($x=0; $x<$no_of_tokens; $x++) {
  if ($tokens[0][$x][0] == END_TOKEN) {
    // End token gevonden, nu terugzoeken naar start token
    $tokens_parsed++;
    $y = $x;
    while ($y > 0) {
      $y--;
      if ($tokens[0][$y][0] == START_TOKEN) {
        $tokens_parsed++;
        // Kijk wat voor tag het is en verberg de start token
        $posStart = $tokens[0][$y][1];
        $posEnd = $tokens[0][$x][1];
        if (preg_match("/^[a-zA-Z]*/",substr($templateContent,$posStart + 1,$posEnd - $posStart - 1),$thistag) == 1) {
          switch (strtolower($thistag[0])) {
            case 'if': $tagtype = TAG_IF; break;
            case 'else': $tagtype = TAG_ELSE; break;
            case 'elseif': $tagtype = TAG_ELSEIF; break;
            case 'loop': $tagtype = TAG_LOOP; break;
            case 'file': $tagtype = TAG_FILE; break;
            case 'php': $tagtype = TAG_PHP; break;
            case 'varfile': $tagtype = TAG_VARFILE; break;
            case 'foreach': $tagtype = TAG_FOREACH; break;
            case '/if': $tagtype = TAG_ENDIF; break;
            case '/foreach': $tagtype = TAG_ENDFORECH; break;
            case '/fe': $tagtype = TAG_ENDFE; break;
            case '/loop': $tagtype = TAG_ENDLOOP; break;
            default: $tagtype = TAG_BAD; // unknown tag
          }
          // push array met (type, positie, data) in $tags array
          array_push($tags,array($tagtype,$posStart+1,substr($templateContent,$posStart+1,$posEnd-$posStart-1)));
        }
        $tokens[0][$y][0] = END_TOKEN;
        $y = -1;
      }
    }
    if ($y == 0) {
      // Geen start token gevonden voor deze end token
      die('geen start token');
    }
  }
}
if ($tokens_parsed != $no_of_tokens) {
  // Een of meerdere end tokens niet gevonden.
  die('geen end token');
}  
$numberoftags = count($tags);

for ($x =0; $x < $numberoftags; $x++) {
  list($type,$pos,$data) = $tags[$x];

  // ongewenste esapce chars wissen
  $data = str_replace(ESCAPE_CHAR.START_TOKEN,START_TOKEN,$data);
  $data = str_replace(ESCAPE_CHAR.END_TOKEN,END_TOKEN,$data);

  switch ($type) {
    case TAG_IF: echo 'IFje: '; break;   // tag if
    case TAG_ELSE: echo 'ELSEtje: '; break; // tag else
    // alle andere cases
    case TAG_BAD: echo 'Foutje: '; break; // niet herkende tag
    default: echo 'blah'; // dit komt als het goed is niet voor
  }
  echo $data."\n"; // inhoud van de tag, alleen voor test doeleinden
}
?>

  • MisterData
  • Registratie: September 2001
  • Laatst online: 19:41
Je mag ook naar mijn source kijken: zoek op www.codebase.nl de stackbased template parser maar es op ;)

  • Apache
  • Registratie: Juli 2000
  • Laatst online: 17-08 14:28

Apache

amateur software devver

Topicstarter
Bedankt voor de source en links naar maar m'n vriend heeft het algo onderhanden genomen en een snelheidswinst van 36* geboekt.

Ik had ook een nieuwe implementatie maar die bleek nog 3* trager dan die van hem, nu nog slechts de kleine optimalisatie's :)

If it ain't broken it doesn't have enough features