GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — develop (#1929)
by
unknown
11:46
created

TemplateHandler::_replacePath()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 1
dl 0
loc 29
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) NAVER <http://www.navercorp.com> */
3
4
/**
5
 * @class TemplateHandler
6
 * @author NAVER ([email protected])
7
 * template compiler
8
 * @version 0.1
9
 * @remarks It compiles template file by using regular expression into php
10
 *          code, and XE caches compiled code for further uses
11
 */
12
class TemplateHandler
13
{
14
15
	private $compiled_path = 'files/cache/template_compiled/'; ///< path of compiled caches files
16
	private $path = NULL; ///< target directory
17
	private $filename = NULL; ///< target filename
18
	private $file = NULL; ///< target file (fullpath)
19
	private $xe_path = NULL;  ///< XpressEngine base path
20
	private $web_path = NULL; ///< tpl file web path
21
	private $compiled_file = NULL; ///< tpl file web path
22
	private $skipTags = NULL;
23
	private $handler_mtime = 0;
24
	static private $rootTpl = NULL;
25
26
	/**
27
	 * constructor
28
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
29
	 */
30
	public function __construct()
31
	{
32
		$this->xe_path = rtrim(preg_replace('/([^\.^\/]+)\.php$/i', '', $_SERVER['SCRIPT_NAME']), '/');
33
		$this->compiled_path = _XE_PATH_ . $this->compiled_path;
34
	}
35
36
	/**
37
	 * returns TemplateHandler's singleton object
38
	 * @return TemplateHandler instance
39
	 */
40
	static public function &getInstance()
41
	{
42
		static $oTemplate = NULL;
43
44
		if(__DEBUG__ == 3)
45
		{
46
			if(!isset($GLOBALS['__TemplateHandlerCalled__']))
47
			{
48
				$GLOBALS['__TemplateHandlerCalled__'] = 1;
49
			}
50
			else
51
			{
52
				$GLOBALS['__TemplateHandlerCalled__']++;
53
			}
54
		}
55
56
		if(!$oTemplate)
57
		{
58
			$oTemplate = new TemplateHandler();
59
		}
60
61
		return $oTemplate;
62
	}
63
64
	/**
65
	 * set variables for template compile
66
	 * @param string $tpl_path
67
	 * @param string $tpl_filename
68
	 * @param string $tpl_file
69
	 * @return void
70
	 */
71
	protected function init($tpl_path, $tpl_filename, $tpl_file = '')
72
	{
73
		// verify arguments
74
		if(substr($tpl_path, -1) != '/')
75
		{
76
			$tpl_path .= '/';
77
		}
78
		if(!is_dir($tpl_path))
79
		{
80
			return;
81
		}
82
		if(!file_exists($tpl_path . $tpl_filename) && file_exists($tpl_path . $tpl_filename . '.html'))
83
		{
84
			$tpl_filename .= '.html';
85
		}
86
87
		// create tpl_file variable
88
		if(!$tpl_file)
89
		{
90
			$tpl_file = $tpl_path . $tpl_filename;
91
		}
92
93
		// set template file infos.
94
		$this->path = $tpl_path;
95
		$this->filename = $tpl_filename;
96
		$this->file = $tpl_file;
97
98
		$this->web_path = $this->xe_path . '/' . ltrim(preg_replace('@^' . preg_quote(_XE_PATH_, '@') . '|\./@', '', $this->path), '/');
99
100
		// get compiled file name
101
		$hash = md5($this->file . __XE_VERSION__);
102
		$this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php";
103
104
		// compare various file's modified time for check changed
105
		$this->handler_mtime = filemtime(__FILE__);
106
107
		$skip = array('');
0 ignored issues
show
Unused Code introduced by
$skip is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
	}
109
110
	/**
111
	 * compiles specified tpl file and execution result in Context into resultant content
112
	 * @param string $tpl_path path of the directory containing target template file
113
	 * @param string $tpl_filename target template file's name
114
	 * @param string $tpl_file if specified use it as template file's full path
115
	 * @return string Returns compiled result in case of success, NULL otherwise
116
	 */
117
	public function compile($tpl_path, $tpl_filename, $tpl_file = '')
118
	{
119
		$buff = false;
120
121
		// store the starting time for debug information
122
		if(__DEBUG__ == 3)
123
		{
124
			$start = getMicroTime();
125
		}
126
127
		// initiation
128
		$this->init($tpl_path, $tpl_filename, $tpl_file);
129
130
		// if target file does not exist exit
131
		if(!$this->file || !file_exists($this->file))
132
		{
133
			return "Err : '{$this->file}' template file does not exists.";
134
		}
135
136
		// for backward compatibility
137
		if(is_null(self::$rootTpl))
138
		{
139
			self::$rootTpl = $this->file;
140
		}
141
142
		$source_template_mtime = filemtime($this->file);
143
		$latest_mtime = $source_template_mtime > $this->handler_mtime ? $source_template_mtime : $this->handler_mtime;
144
145
		// cache control
146
		$oCacheHandler = CacheHandler::getInstance('template');
147
148
		// get cached buff
149
		if($oCacheHandler->isSupport())
150
		{
151
			$cache_key = 'template:' . $this->file;
152
			$buff = $oCacheHandler->get($cache_key, $latest_mtime);
153
		}
154
		else
155
		{
156
			if(is_readable($this->compiled_file) && filemtime($this->compiled_file) > $latest_mtime && filesize($this->compiled_file))
157
			{
158
				$buff = 'file://' . $this->compiled_file;
159
			}
160
		}
161
162
		if($buff === FALSE)
163
		{
164
			$buff = $this->parse();
165
			if($oCacheHandler->isSupport())
166
			{
167
				$oCacheHandler->put($cache_key, $buff);
0 ignored issues
show
Bug introduced by
The variable $cache_key does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
168
			}
169
			else
170
			{
171
				FileHandler::writeFile($this->compiled_file, $buff);
172
			}
173
		}
174
175
		$output = $this->_fetch($buff);
0 ignored issues
show
Bug introduced by
It seems like $buff can also be of type boolean or null; however, TemplateHandler::_fetch() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
176
177
		if($__templatehandler_root_tpl == $this->file)
0 ignored issues
show
Bug introduced by
The variable $__templatehandler_root_tpl seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
178
		{
179
			$__templatehandler_root_tpl = null;
0 ignored issues
show
Unused Code introduced by
$__templatehandler_root_tpl is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
180
		}
181
182
		// store the ending time for debug information
183
		if(__DEBUG__ == 3)
184
		{
185
			$GLOBALS['__template_elapsed__'] += getMicroTime() - $start;
0 ignored issues
show
Bug introduced by
The variable $start does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
186
		}
187
188
		return $output;
189
	}
190
191
	/**
192
	 * compile specified file and immediately return
193
	 * @param string $tpl_path path of the directory containing target template file
194
	 * @param string $tpl_filename target template file's name
195
	 * @return string Returns compiled content in case of success or NULL in case of failure
196
	 */
197
	public function compileDirect($tpl_path, $tpl_filename)
198
	{
199
		$this->init($tpl_path, $tpl_filename, null);
200
201
		// if target file does not exist exit
202
		if(!$this->file || !file_exists($this->file))
203
		{
204
			Context::close();
205
			exit("Cannot find the template file: '{$this->file}'");
206
		}
207
208
		return $this->parse();
209
	}
210
211
	/**
212
	 * parse syntax.
213
	 * @param string $buff template file
214
	 * @return string compiled result in case of success or NULL in case of error
215
	 */
216
	protected function parse($buff = null)
217
	{
218
		if(is_null($buff))
219
		{
220
			if(!is_readable($this->file))
221
			{
222
				return;
223
			}
224
225
			// read tpl file
226
			$buff = FileHandler::readFile($this->file);
227
		}
228
229
		// HTML tags to skip
230
		if(is_null($this->skipTags))
231
		{
232
			$this->skipTags = array('marquee');
233
		}
234
235
		// replace comments
236
		$buff = preg_replace('@<!--//.*?-->@s', '', $buff);
237
238
		// replace value of src in img/input/script tag
239
		$buff = preg_replace_callback('/<(?:img|input|script)(?:[^<>]*?)(?(?=cond=")(?:cond="[^"]+"[^<>]*)+|)[^<>]* src="(?!(?:https?|file):\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff);
240
241
		// replace loop and cond template syntax
242
		$buff = $this->_parseInline($buff);
243
244
		// include, unload/load, import
245
		$buff = preg_replace_callback('/{(@[\s\S]+?|(?=\$\w+|_{1,2}[A-Z]+|[!\(+-]|\w+(?:\(|::)|\d+|[\'"].*?[\'"]).+?)}|<(!--[#%])?(include|import|(un)?load(?(4)|(?:_js_plugin)?))(?(2)\(["\']([^"\']+)["\'])(.*?)(?(2)\)--|\/)>|<!--(@[a-z@]*)([\s\S]*?)-->(\s*)/', array($this, '_parseResource'), $buff);
246
247
		// remove block which is a virtual tag
248
		$buff = preg_replace('@</?block\s*>@is', '', $buff);
249
250
		// form auto generation
251
		$temp = preg_replace_callback('/(<form(?:<\?php.+?\?>|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this, '_compileFormAuthGeneration'), $buff);
252
		if($temp)
253
		{
254
			$buff = $temp;
255
		}
256
257
		// prevent from calling directly before writing into file
258
		$buff = '<?php if(!defined("__XE__"))exit;?>' . $buff;
259
260
		// remove php script reopening
261
		$buff = preg_replace(array('/(\n|\r\n)+/', '/(;)?( )*\?\>\<\?php([\n\t ]+)?/'), array("\n", ";\n"), $buff);
262
263
		return $buff;
264
	}
265
266
	/**
267
	 * preg_replace_callback handler
268
	 * 1. remove ruleset from form tag
269
	 * 2. add hidden tag with ruleset value
270
	 * 3. if empty default hidden tag, generate hidden tag (ex:mid, vid, act...)
271
	 * 4. generate return url, return url use in server side validator
272
	 * @param array $matches
273
	 * @return string
274
	 */
275
	private function _compileFormAuthGeneration($matches)
276
	{
277
		// form ruleset attribute move to hidden tag
278
		if($matches[1])
279
		{
280
			preg_match('/ruleset="([^"]*?)"/is', $matches[1], $m);
281
			if($m[0])
282
			{
283
				$matches[1] = preg_replace('/' . addcslashes($m[0], '?$') . '/i', '', $matches[1]);
284
285
				if(strpos($m[1], '@') !== FALSE)
286
				{
287
					$path = str_replace('@', '', $m[1]);
288
					$path = './files/ruleset/' . $path . '.xml';
289
				}
290
				else if(strpos($m[1], '#') !== FALSE)
291
				{
292
					$fileName = str_replace('#', '', $m[1]);
293
					$fileName = str_replace('<?php echo ', '', $fileName);
294
					$fileName = str_replace(' ?>', '', $fileName);
295
					$path = '#./files/ruleset/' . $fileName . '.xml';
296
297
					preg_match('@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm);
298
					$module_path = $mm[1];
299
					list($rulsetFile) = explode('.', $fileName);
300
					$autoPath = $module_path . '/ruleset/' . $rulsetFile . '.xml';
301
					$m[1] = $rulsetFile;
302
				}
303
				else if(preg_match('@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm))
304
				{
305
					$module_path = $mm[1];
306
					$path = $module_path . '/ruleset/' . $m[1] . '.xml';
307
				}
308
309
				$matches[2] = '<input type="hidden" name="ruleset" value="' . $m[1] . '" />' . $matches[2];
310
				//assign to addJsFile method for js dynamic recache
311
				$matches[1] = '<?php Context::addJsFile("' . $path . '", FALSE, "", 0, "body", TRUE, "' . $autoPath . '") ?' . '>' . $matches[1];
0 ignored issues
show
Bug introduced by
The variable $path does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $autoPath does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
312
			}
313
		}
314
315
		// if not exists default hidden tag, generate hidden tag
316
		preg_match_all('/<input[^>]* name="(act|mid|vid)"/is', $matches[2], $m2);
317
		$checkVar = array('act', 'mid', 'vid');
318
		$resultArray = array_diff($checkVar, $m2[1]);
319
		if(is_array($resultArray))
320
		{
321
			$generatedHidden = '';
322
			foreach($resultArray AS $key => $value)
323
			{
324
				$generatedHidden .= '<input type="hidden" name="' . $value . '" value="<?php echo $__Context->' . $value . ' ?>" />';
325
			}
326
			$matches[2] = $generatedHidden . $matches[2];
327
		}
328
329
		// return url generate
330
		if(!preg_match('/no-error-return-url="true"/i', $matches[1]))
331
		{
332
			preg_match('/<input[^>]*name="error_return_url"[^>]*>/is', $matches[2], $m3);
333
			if(!$m3[0])
334
				$matches[2] = '<input type="hidden" name="error_return_url" value="<?php echo htmlspecialchars(getRequestUriByServerEnviroment(), ENT_COMPAT | ENT_HTML401, \'UTF-8\', false) ?>" />' . $matches[2];
335
		}
336
		else
337
		{
338
			$matches[1] = preg_replace('/no-error-return-url="true"/i', '', $matches[1]);
339
		}
340
341
		$matches[0] = '';
342
		return implode($matches);
343
	}
344
345
	/**
346
	 * fetch using ob_* function
347
	 * @param string $buff if buff is not null, eval it instead of including compiled template file
348
	 * @return string
349
	 */
350
	private function _fetch($buff)
351
	{
352
		if(!$buff)
353
		{
354
			return;
355
		}
356
357
		$__Context = &$GLOBALS['__Context__'];
358
		$__Context->tpl_path = $this->path;
359
360
		if($_SESSION['is_logged'])
361
		{
362
			$__Context->logged_info = Context::get('logged_info');
363
		}
364
365
		$level = ob_get_level();
366
		ob_start();
367
		if(substr($buff, 0, 7) == 'file://')
368
		{
369
			if(__DEBUG__)
370
			{
371
				//load cache file from disk
372
				$eval_str = FileHandler::readFile(substr($buff, 7));
373
				$eval_str_buffed = "?>" . $eval_str;
374
				@eval($eval_str_buffed);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
375
				$error_info = error_get_last();
376
				//parse error
377
				if ($error_info['type'] == 4)
378
				{
379
				    throw new Exception("Error Parsing Template - {$error_info['message']} in template file {$this->file}");
380
				}
381
			}
382
			else
383
			{
384
				include(substr($buff, 7));
385
			}
386
		}
387
		else
388
		{
389
			$eval_str = "?>" . $buff;
390
			@eval($eval_str);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
391
			$error_info = error_get_last();
392
			//parse error
393
			if ($error_info['type'] == 4)
394
			{
395
			    throw new Exception("Error Parsing Template - {$error_info['message']} in template file {$this->file}");
396
			}
397
		}
398
399
		$contents = '';
400
		while (ob_get_level() - $level > 0) {
401
			$contents .= ob_get_contents();
402
			ob_end_clean();
403
		}
404
		return $contents;
405
	}
406
407
	/**
408
	 * preg_replace_callback hanlder
409
	 *
410
	 * replace image path
411
	 * @param array $match
412
	 *
413
	 * @return string changed result
414
	 */
415
	private function _replacePath($match)
416
	{
417
		//return origin conde when src value started '${'.
418
		if(preg_match('@^\${@', $match[1]))
419
		{
420
			return $match[0];
421
		}
422
423
		//return origin code when src value include variable.
424
		if(preg_match('@^[\'|"]\s*\.\s*\$@', $match[1]))
425
		{
426
			return $match[0];
427
		}
428
429
		$src = preg_replace('@^(\./)+@', '', trim($match[1]));
430
431
		$src = $this->web_path . $src;
432
		$src = str_replace('/./', '/', $src);
433
434
		// for backward compatibility
435
		$src = preg_replace('@/((?:[\w-]+/)+)\1@', '/\1', $src);
436
437
		while(($tmp = preg_replace('@[^/]+/\.\./@', '', $src, 1)) !== $src)
438
		{
439
			$src = $tmp;
440
		}
441
442
		return substr($match[0], 0, -strlen($match[1]) - 6) . "src=\"{$src}\"";
443
	}
444
445
	/**
446
	 * replace loop and cond template syntax
447
	 * @param string $buff
448
	 * @return string changed result
449
	 */
450
	private function _parseInline($buff)
451
	{
452
		if(preg_match_all('/<([a-zA-Z]+\d?)(?>(?!<[a-z]+\d?[\s>]).)*?(?:[ \|]cond| loop)="/s', $buff, $match) === false)
453
		{
454
			return $buff;
455
		}
456
457
		$tags = array_diff(array_unique($match[1]), $this->skipTags);
458
459
		if(!count($tags))
460
		{
461
			return $buff;
462
		}
463
464
		$tags = '(?:' . implode('|', $tags) . ')';
465
		$split_regex = "@(<(?>/?{$tags})(?>[^<>\{\}\"']+|<!--.*?-->|{[^}]+}|\".*?\"|'.*?'|.)*?>)@s";
466
467
		$nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE);
468
469
		// list of self closing tags
470
		$self_closing = array('area' => 1, 'base' => 1, 'basefont' => 1, 'br' => 1, 'hr' => 1, 'input' => 1, 'img' => 1, 'link' => 1, 'meta' => 1, 'param' => 1, 'frame' => 1, 'col' => 1);
471
472
		for($idx = 1, $node_len = count($nodes); $idx < $node_len; $idx+=2)
473
		{
474
			if(!($node = $nodes[$idx]))
475
			{
476
				continue;
477
			}
478
479
			if(preg_match_all('@\s(loop|cond)="([^"]+)"@', $node, $matches))
480
			{
481
				// this tag
482
				$tag = substr($node, 1, strpos($node, ' ') - 1);
483
484
				// if the vale of $closing is 0, it means 'skipping'
485
				$closing = 0;
486
487
				// process opening tag
488
				foreach($matches[1] as $n => $stmt)
489
				{
490
					$expr = $matches[2][$n];
491
					$expr = $this->_replaceVar($expr);
492
					$closing++;
493
494
					switch($stmt)
495
					{
496
						case 'cond':
497
							$nodes[$idx - 1] .= "<?php if({$expr}){ ?>";
498
							break;
499
						case 'loop':
500
							if(!preg_match('@^(?:(.+?)=>(.+?)(?:,(.+?))?|(.*?;.*?;.*?)|(.+?)\s*=\s*(.+?))$@', $expr, $expr_m))
501
							{
502
								break;
503
							}
504
							if($expr_m[1])
505
							{
506
								$expr_m[1] = trim($expr_m[1]);
507
								$expr_m[2] = trim($expr_m[2]);
508
								if($expr_m[3])
509
								{
510
									$expr_m[2] .= '=>' . trim($expr_m[3]);
511
								}
512
								$nodes[$idx - 1] .= "<?php if({$expr_m[1]}&&count({$expr_m[1]}))foreach({$expr_m[1]} as {$expr_m[2]}){ ?>";
513
							}
514
							elseif($expr_m[4])
515
							{
516
								$nodes[$idx - 1] .= "<?php for({$expr_m[4]}){ ?>";
517
							}
518
							elseif($expr_m[5])
519
							{
520
								$nodes[$idx - 1] .= "<?php while({$expr_m[5]}={$expr_m[6]}){ ?>";
521
							}
522
							break;
523
					}
524
				}
525
				$node = preg_replace('@\s(loop|cond)="([^"]+)"@', '', $node);
526
527
				// find closing tag
528
				$close_php = '<?php ' . str_repeat('}', $closing) . ' ?>';
529
				//  self closing tag
530
				if($node{1} == '!' || substr($node, -2, 1) == '/' || isset($self_closing[$tag]))
531
				{
532
					$nodes[$idx + 1] = $close_php . $nodes[$idx + 1];
533
				}
534
				else
535
				{
536
					$depth = 1;
537
					for($i = $idx + 2; $i < $node_len; $i+=2)
538
					{
539
						$nd = $nodes[$i];
540
						if(strpos($nd, $tag) === 1)
541
						{
542
							$depth++;
543
						}
544
						elseif(strpos($nd, '/' . $tag) === 1)
545
						{
546
							$depth--;
547
							if(!$depth)
548
							{
549
								$nodes[$i - 1] .= $nodes[$i] . $close_php;
550
								$nodes[$i] = '';
551
								break;
552
							}
553
						}
554
					}
555
				}
556
			}
557
558
			if(strpos($node, '|cond="') !== false)
559
			{
560
				$node = preg_replace('@(\s[-\w:]+(?:="[^"]+?")?)\|cond="(.+?)"@s', '<?php if($2){ ?>$1<?php } ?>', $node);
561
				$node = $this->_replaceVar($node);
562
			}
563
564
			if($nodes[$idx] != $node)
565
			{
566
				$nodes[$idx] = $node;
567
			}
568
		}
569
570
		$buff = implode('', $nodes);
571
572
		return $buff;
573
	}
574
575
	/**
576
	 * preg_replace_callback hanlder
577
	 * replace php code.
578
	 * @param array $m
579
	 * @return string changed result
580
	 */
581
	private function _parseResource($m)
582
	{
583
		// {@ ... } or {$var} or {func(...)}
584
		if($m[1])
585
		{
586
			if(preg_match('@^(\w+)\(@', $m[1], $mm) && !function_exists($mm[1]))
587
			{
588
				return $m[0];
589
			}
590
591
			$echo = 'echo ';
592
			if($m[1]{0} == '@')
593
			{
594
				$echo = '';
595
				$m[1] = substr($m[1], 1);
596
			}
597
			return '<?php ' . $echo . $this->_replaceVar($m[1]) . ' ?>';
598
		}
599
600
		if($m[3])
601
		{
602
			$attr = array();
603
			if($m[5])
604
			{
605 View Code Duplication
				if(preg_match_all('@,(\w+)="([^"]+)"@', $m[6], $mm))
606
				{
607
					foreach($mm[1] as $idx => $name)
608
					{
609
						$attr[$name] = $mm[2][$idx];
610
					}
611
				}
612
				$attr['target'] = $m[5];
613
			}
614 View Code Duplication
			else
615
			{
616
				if(!preg_match_all('@ (\w+)="([^"]+)"@', $m[6], $mm))
617
				{
618
					return $m[0];
619
				}
620
				foreach($mm[1] as $idx => $name)
621
				{
622
					$attr[$name] = $mm[2][$idx];
623
				}
624
			}
625
626
			switch($m[3])
627
			{
628
				// <!--#include--> or <include ..>
629
				case 'include':
630
					if(!$this->file || !$attr['target'])
631
					{
632
						return '';
633
					}
634
635
					$pathinfo = pathinfo($attr['target']);
636
					$fileDir = $this->_getRelativeDir($pathinfo['dirname']);
637
638
					if(!$fileDir)
639
					{
640
						return '';
641
					}
642
643
					return "<?php \$__tpl=TemplateHandler::getInstance();echo \$__tpl->compile('{$fileDir}','{$pathinfo['basename']}') ?>";
644
				// <!--%load_js_plugin-->
645
				case 'load_js_plugin':
646
					$plugin = $this->_replaceVar($m[5]);
647
					$s = "<!--#JSPLUGIN:{$plugin}-->";
648
					if(strpos($plugin, '$__Context') === false)
649
					{
650
						$plugin = "'{$plugin}'";
651
					}
652
653
					$s .= "<?php Context::loadJavascriptPlugin({$plugin}); ?>";
654
					return $s;
655
				// <load ...> or <unload ...> or <!--%import ...--> or <!--%unload ...-->
656
				case 'import':
657
				case 'load':
658
				case 'unload':
659
					$metafile = '';
660
					$pathinfo = pathinfo($attr['target']);
661
					$doUnload = ($m[3] === 'unload');
662
					$isRemote = !!preg_match('@^(https?:)?//@i', $attr['target']);
663
664
					if(!$isRemote)
665
					{
666
						if(!preg_match('@^\.?/@', $attr['target']))
667
						{
668
							$attr['target'] = './' . $attr['target'];
669
						}
670
						if(substr($attr['target'], -5) == '/lang')
671
						{
672
							$pathinfo['dirname'] .= '/lang';
673
							$pathinfo['basename'] = '';
674
							$pathinfo['extension'] = 'xml';
675
						}
676
677
						$relativeDir = $this->_getRelativeDir($pathinfo['dirname']);
678
679
						$attr['target'] = $relativeDir . '/' . $pathinfo['basename'];
680
					}
681
682
					switch($pathinfo['extension'])
683
					{
684
						case 'xml':
685
							if($isRemote || $doUnload)
686
							{
687
								return '';
688
							}
689
							// language file?
690
							if($pathinfo['basename'] == 'lang.xml' || substr($pathinfo['dirname'], -5) == '/lang')
691
							{
692
								$result = "Context::loadLang('{$relativeDir}');";
0 ignored issues
show
Bug introduced by
The variable $relativeDir does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
693
							}
694
							else
695
							{
696
								$result = "require_once('./classes/xml/XmlJsFilter.class.php');\$__xmlFilter=new XmlJsFilter('{$relativeDir}','{$pathinfo['basename']}');\$__xmlFilter->compile();";
697
							}
698
							break;
699 View Code Duplication
						case 'js':
700
							if($doUnload)
701
							{
702
								$result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}');";
703
							}
704
							else
705
							{
706
								$metafile = $attr['target'];
707
								$result = "\$__tmp=array('{$attr['target']}','{$attr['type']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
708
							}
709
							break;
710 View Code Duplication
						case 'css':
711
							if($doUnload)
712
							{
713
								$result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');";
714
							}
715
							else
716
							{
717
								$metafile = $attr['target'];
718
								$result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
719
							}
720
							break;
721
					}
722
723
					$result = "<?php {$result} ?>";
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
724
					if($metafile)
725
					{
726
						$result = "<!--#Meta:{$metafile}-->" . $result;
727
					}
728
729
					return $result;
730
			}
731
		}
732
733
		// <[email protected]> such as <!--@if($cond)-->, <!--@else-->, <!--@end-->
734
		if($m[7])
735
		{
736
			$m[7] = substr($m[7], 1);
737 View Code Duplication
			if(!$m[7])
738
			{
739
				return '<?php ' . $this->_replaceVar($m[8]) . '{ ?>' . $m[9];
740
			}
741
			if(!preg_match('/^(?:((?:end)?(?:if|switch|for(?:each)?|while)|end)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[7], $mm))
742
			{
743
				return '';
744
			}
745
			if($mm[1])
746
			{
747
				if($mm[1]{0} == 'e')
748
				{
749
					return '<?php } ?>' . $m[9];
750
				}
751
752
				$precheck = '';
753
				if($mm[1] == 'switch')
754
				{
755
					$m[9] = '';
756
				}
757
				elseif($mm[1] == 'foreach')
758
				{
759
					$var = preg_replace('/^\s*\(\s*(.+?) .*$/', '$1', $m[8]);
760
					$precheck = "if({$var}&&count({$var}))";
761
				}
762
				return '<?php ' . $this->_replaceVar($precheck . $m[7] . $m[8]) . '{ ?>' . $m[9];
763
			}
764 View Code Duplication
			if($mm[2])
765
			{
766
				return "<?php }{$m[7]}" . $this->_replaceVar($m[8]) . "{ ?>" . $m[9];
767
			}
768
			if($mm[4])
769
			{
770
				return "<?php " . ($mm[3] ? 'break;' : '') . "{$m[7]} " . trim($m[8], '()') . ": ?>" . $m[9];
771
			}
772
			if($mm[5])
773
			{
774
				return "<?php break; ?>";
775
			}
776
			return '';
777
		}
778
		return $m[0];
779
	}
780
781
	/**
782
	 * change relative path
783
	 * @param string $path
784
	 * @return string
785
	 */
786
	function _getRelativeDir($path)
787
	{
788
		$_path = $path;
789
790
		$fileDir = strtr(realpath($this->path), '\\', '/');
791
		if($path{0} != '/')
792
		{
793
			$path = strtr(realpath($fileDir . '/' . $path), '\\', '/');
794
		}
795
796
		// for backward compatibility
797
		if(!$path)
798
		{
799
			$dirs = explode('/', $fileDir);
800
			$paths = explode('/', $_path);
801
			$idx = array_search($paths[0], $dirs);
802
803
			if($idx !== false)
804
			{
805
				while($dirs[$idx] && $dirs[$idx] === $paths[0])
806
				{
807
					array_splice($dirs, $idx, 1);
808
					array_shift($paths);
809
				}
810
				$path = strtr(realpath($fileDir . '/' . implode('/', $paths)), '\\', '/');
811
			}
812
		}
813
814
		$path = preg_replace('/^' . preg_quote(_XE_PATH_, '/') . '/', '', $path);
815
816
		return $path;
817
	}
818
819
	/**
820
	 * replace PHP variables of $ character
821
	 * @param string $php
822
	 * @return string $__Context->varname
823
	 */
824
	function _replaceVar($php)
825
	{
826
		if(!strlen($php))
827
		{
828
			return '';
829
		}
830
		return preg_replace('@(?<!::|\\\\|(?<!eval\()\')\$([a-z]|_[a-z0-9])@i', '\$__Context->$1', $php);
831
	}
832
833
}
834
/* End of File: TemplateHandler.class.php */
835
/* Location: ./classes/template/TemplateHandler.class.php */
836