[ create a new paste ] login | about

Link: http://codepad.org/ARGwQPzA    [ raw code | fork ]

k4st - PHP, pasted on Dec 10:
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
<?php

/**
 * $header
 * @copyright Copyright 2007, Peter Goodman
 * @package valkyrie
 * @subpackage Template.parser
 * @author Peter Goodman
 */

define ('VK_TPL_TAGS_DIR', dirname(__FILE__) .'/tags/');

/**
 * Parse a document for specific XML tags.
 * @see Response
 */
class TemplateParser {
    
    /**
     * Get a tag handler class name. If the tag handler doesn't exist, then
     * use the UnknownNode tag handler.
     * @param string $tag_name The tag name of the tag handler to look for.
     * @return string The class name of the tag handler to use.
     * @see UnknownNode, ClosingNode, NonClosingNode
     * @internal
     */
    private function getTagHandler($prefix = '', $tag_name)
    {
        // default to the unknown node tag handler
        $ret = 'UnknownNode';
                
        $file = VK_TPL_TAGS_DIR .'/'. $prefix .'/'. $tag_name .'.php';
        if(file_exists($file)) {
            require_once $file;
            
            $class = ucfirst($prefix) . camelize($tag_name) .'Node';
            
            if(class_exists($class, FALSE)) {
                $ret = $class;
            }
        }
        
        return new $ret($prefix, $tag_name);
    }
    
    /**
     * Parse the contents of a file and return the parsed text.
     * @param string $file_name The absolute path to the file to parse.
     * @return string The parsed file contents.
     * @see TemplateParser::parseTags(), TemplateParser::parseVariables()
     */
    public function parse($file_name = '')
    {
        $buffer = '';
        
        if(file_exists($file_name)) {
            $buffer = file_get_contents($file_name);
            $buffer = $this->parseTags($buffer);        
            $buffer = $this->parseVariables($buffer);
        }
                
        return $buffer;
    }
    
    /**
     * Parse out the template tags.
     * @param string $buffer The text to parse.
     * @return string The parsed text.
     * @internal
     */
    private function parseTags($buffer = '')
    {
        // split up the buffer into tags and text
        $parts = preg_split("~<(/?)([a-z0-9_]+)\:([a-z0-9_]+)((?: [^>]*)?)>~i", $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);
        
        // create a node stack, this will keep track of
        // non-text nodes, and push our root node onto
        // it as the first node.
        $stack = array();
        $stack[] = new RootNode('');
                
        // loop through the split up parts of the buffer and
        // also increment a tracker
        $i = -1;
        while(isset($parts[++$i])) {

            // get the last node added to the stack
            $parent = end($stack);
                        
            // 0 for text, the 1-4 for tag info
            $key = $i % 5;
            
            // stuff before
            if($key == 0) {
                $parent->buffer($parts[$i]);
            }
            
            // do we have a tag?
            if(isset($parts[$i+4])) {
                
                // get the tag info
                $closing = trim($parts[$i+1]) == '/';
                $prefix = trim($parts[$i+2]);
                $tag_name = trim($parts[$i+3]);
                $attribs = trim($parts[$i+4]);
                $non_closing = FALSE;
                
                // this tag is non-closing
                if($attribs != '' && $attribs{strlen($attribs)-1} == '/') {
                    $non_closing = TRUE;
                    $attribs = trim(substr($attribs, 0, -1));
                }
                
                // closing tag
                if($closing) {
                    $tag = array_pop($stack);
                    $parent = end($stack);
                    
                    // right closing tag
                    if($tag_name == $tag->getName()) {
                        $parent->buffer($tag->parseTag());
                    
                    // wrong closing tag
                    } else {
                        $parent->buffer('<!-- BAD CLOSING TAG FOR ['. $prefix .':'. $tag_name .'] -->');
                    }
                
                // opening tag
                } else {
                    
                    // get the tag handler
                    $tag = $this->getTagHandler($prefix, $tag_name);
                    
                    // parse out the attributes
                    if($attribs != '') {
                        preg_match_all('~(?P<attrib>[a-z]+)="(?P<value>[^"]*)"~i', $attribs, $attributes);
                        $attributes = $this->parseTagAttributes($attributes);
                        
                        if(!empty($attributes)) {
                            $tag->addAttributes($attributes);
                        }
                    }
                    
                    // non-closing node, parse it right away and add it to the parent
                    // nodes buffer
                    if($non_closing) {
                        $parent->buffer($tag->parseTag());
                    
                    // opening tag of a closing node, add it to the stack
                    } else {
                        $stack[] = $tag;
                    }
                }
            }
            
            $i+= 4;
        }
        
        // parse out any tags that somehow weren't properly parsed. If there were
        // any tags that weren't properly closed, this will catch them.
        $parsed_buffer = '';
        $temp_buffer = '';
        while($node = array_pop($stack)) {
        
            // is this the root node?
            if($node instanceof RootNode) {
                // add any text back into it from malformed
                // tags then parse it.
                $node->buffer($temp_buffer);
                
                // parse the root node
                $parsed_buffer = $node->parseTag();
            }
            
            // extract the buffers from the malformed tags
            // to put them back into the root node later.
            else {
                $temp_buffer .= $node->buffer();
            }
        }
        
        return $parsed_buffer;
    }
    
    /**
     * Parse the tag attributes.
     * @param array $parts The tag attributes.
     * @return array An associative array of attributes => value
     * @internal
     */
    private function parseTagAttributes(array $parts = array())
    {       
        $attributes = array();
            
        foreach($parts['attrib'] as $i => $attrib) {
            $attributes[strtolower($attrib)] = $parts['value'][$i];
        }
        
        return $attributes;
    }
    
    /**
     * Handle matched template variables.
     * @param array $matches An array returned from a preg_replace_callback
     * @return string A compiled version of the template variable.
     * @see TemplateParser::parseVariables, preg_replace_callback
     * @internal
     */
    private function handleVariable($matches) {
                
        $matches[2] = trim($matches[2]);
        $buffer = '';
        
        // template variable
        if($matches[1] == '$') {
            
            if($matches[2] != '') {
                $parts = explode('|', $matches[2]);
                $varname = array_shift($parts);
                
                $buffer = '$scope["'. $varname .'"]';
                
                foreach($parts as $callback) {
        
                    // call a callback if it exists
                    if($callback !== NULL) {
                        $callback_args = explode(":", $callback);
                        $callback = array_shift($callback_args);
                    
                        if(function_exists($callback)) {
                            $callback_args = !empty($callback_args) ? ','. implode(",", $callback_args) : '';
                            $buffer = $callback .'('. $buffer . $callback_args .')';
                        }
                    }
                }
                
                if($matches[1] == '$') {
                    $buffer = '<?='. $buffer .';?>';
                }
            }
        }
        
        // url / path variable
        else if($matches[1] == '/') {
            if($matches[0] == '{/}') {
                $matches[2] = '/';
            }
            
            $buffer = vk_url(vk_path($matches[2]));
            
            // need to put $ in subpattern so that handleVariable accepts it properly :P
            $buffer = preg_replace_callback("~(\\$)([^/]+)~", array($this, 'handleVariable'), $buffer);
        }
        
        // get text
        else {
            
            $buffer = '';
        }
        
        return $buffer;
    }
    
    /**
     * Find and parse variables in the text.
     * @param string $buffer The text to parse.
     * @return string The text with all matched variables compiled.
     * @see TemplateParser::parseVariables, preg_replace_callback
     * @internal
     */
    private function parseVariables($buffer)
    {
        $buffer = preg_replace_callback('~{(\$|\@|\/)([^}]*)}~', array($this, 'handleVariable'), $buffer);      
        return $buffer;
    }
}

/**
 * Exception for template nodes, gets called for missing attributes.
 */
class TemplateNodeException extends Exception {
    
}

/**
 * A template node, this is any xml tag that uses a namespace,
 * for example: <tpl:something>
 */
abstract class TemplateNode implements ArrayAccess {
    
    /**
     * The text in-between the opening and closing nodes od this tag.
     * @internal
     */
    private $buffer = '';
    
    /**
     * This tags name.
     * @internal
     */
    protected $name = '',
            $prefix = '';
    
    /**
     * An associative array of this tags attributes.
     * @internal
     */
    private $attributes = array();
    
    /**
     * Constructor, pass in this tags name.
     * @param string $name The tag name.
     */
    public function __construct($prefix = '', $name = '') {
        $this->prefix = $prefix;
        $this->name = $name;
    }
    
    /**
     * Add the tags attributes to the attributes array.
     * @param array $array An associative array of attribute=>value.
     */
    public function addAttributes(array $array = array()) {
        $this->attributes = array_merge($this->attributes, $array);
    }
    
    /**
     * Add text to this tags buffer.
     * @param string $buffer The text to append to the buffer.
     * @see ClosingNode::$buffer
     * @internal
     */
    public function buffer($buffer = FALSE) {
        if($buffer !== FALSE) {
            $this->buffer .= $buffer;
        } else {
            return $this->buffer;
        }
    }
    
    /**
     * Get the name of this tag.
     * @return string The tag name.
     */
    public function getName() {
        return $this->name;
    }
    
    /**
     * Require that this tag be passed certain arguments.
     * <code>
     * $this->requireAttributes('href', 'title', ...);
     * </code>
     */
    public function requireAttributes() {
        foreach(func_get_args() as $attrib) {
            if(!array_key_exists($attrib, $this->attributes)) {
                throw new TemplateNodeException("Missing attribute [$attrib] for tag [{$this->name}].");
            }
        }
    }
    
    /**
     * Require at least one out of a number of attributes to exist.
     * <code>
     * $this->requireOne('href', 'src');
     * </code>
     * @return boolean The first present attribute from the set found.
     */
    public function requireOne() {
        $ret = FALSE;
        $attribs = func_get_args();
        foreach($attribs as $attrib) {
            if(array_key_exists($attrib, $this->attributes)) {
                $ret = $attrib;
                break;
            }
        }
        
        if($ret === FALSE) {
            throw new TemplateNodeException("Missing at least one required attribute of set [". implode(',', $attribs) ."] for tag [{$this->name}].");
        }
        
        return $ret;
    }
    
    /**
     * Get an attribute.
     * @param string $key The attribute name.
     * @return mixed The value of the attribute.
     * @see ArrayAccess
     */
    public function offsetGet($key) {
        return $this->offsetExists($key) ? $this->attributes[$key] : NULL;
    }
    
    /**
     * Set an attribute.
     * @param string $key The attribute name.
     * @param mixed $val The value of the attribute to set.
     * @see ArrayAccess
     */
    public function offsetSet($key, $val) {
        $this->attributes[$key] = $val;
    }
    
    /**
     * Check if an attribute exists.
     * @param string $key The attribute name.
     * @return boolean Whether or not the attribute exists.
     * @see ArrayAccess
     */
    public function offsetExists($key) {
        return isset($this->attributes[$key]);
    }
    
    /**
     * Unset an attribute.
     * @param string $key The attribute name.
     * @see ArrayAccess
     */
    public function offsetUnset($key) {
        unset($this->attributes[$key]);
    }
    
    /**
     * Parse the tag.
     */
    abstract public function parseTag();
}

/**
 * Class to handle the root node in the template parser stack.
 */
class RootNode extends TemplateNode {
    public function parseTag() {
        return $this->buffer();
    }
}

/**
 * Class to handle nodes that the parser doesn't recognize.
 */
class UnknownNode extends TemplateNode {
    public function parseTag() {
        
        $buffer = "<". $this->prefix .":". $this->name .">\n";
        $buffer .= $this->buffer();
        $buffer .= "\n</". $this->prefix .':'. $this->name .'>';
        
        return $buffer;
    }
}


Create a new paste based on this one


Comments: