|
Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon. |
|
View previous topic :: View next topic |
Author |
Message |
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Tue Feb 01, 2011 5:13 pm Post subject: Problem with compile filepath |
|
|
Code: |
include 'Smarty.class.php';
$smarty=new Smarty();
//it's production, so compile check is off
$this->setCompileCheck(true);
//set absolute path for template dir
$smarty->setTemplateDir(dirname(__FILE__).'/tpl/subdir');
//use relative path
//it's becomes generate compiled file
$smarty->display('../main.tpl');
//OK let's try using SAME template again
//WOW! we've got another compiled file
$smarty->display(dirname(__FILE__).'/tpl/../tpl/main.tpl');
//well, we changed some templates and want to update compiled files (becouse compile check is off)
$smarty->setTemplateDir(dirname(__FILE__).'/tpl');
$smarty->compileAllTemplates();
//Oops - we got one more compiled file
|
first time stored filepath is
/home/www/tpl/subdir/../main.tpl
second
/home/www/tpl/../tpl/main.tpl
and the last
/home/www/tpl/main.tpl
Maybe it's better change file_exists() by realpath() while filepath generates? |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Feb 01, 2011 6:46 pm Post subject: |
|
|
using realpath() yields another stat() (filesystem I/O). These are generally very slow and should be omitted wherever possible.
Without having really looked into this (lacking the time atm), is there another fix to the issue? |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Tue Feb 01, 2011 7:49 pm Post subject: |
|
|
the first thing that came to mind:
Code: |
$path='/home/test2.ru/./www/test/../tpl/../../tpl/main.tpl';
$npath='';
while($npath!=$path)
{
$npath=$path;
$path = preg_replace('%[^/]+/\.\./%', '', $path);
}
$path = preg_replace('%/\./%', '/', $path);
echo $path; // result is /home/test2.ru/tpl/main.tpl
|
but i think it's possible without regExp |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Feb 01, 2011 8:43 pm Post subject: |
|
|
Code: | $cwd = '/blarp/bloo';
$paths = array(
'/foo/bar/../one/foo.tpl' => '/foo/one/foo.tpl',
'/foo/bar/../../two/foo.tpl' => '/two/foo.tpl',
'/foo/bar/.././three/foo.tpl' => '/foo/three/foo.tpl',
'/foo/bar/.././../four/foo.tpl' => '/four/foo.tpl',
'/foo/bar/../bla/../five/foo.tpl' => '/foo/five/foo.tpl',
'./six/foo.tpl' => '/blarp/bloo/six/foo.tpl',
'../seven/foo.tpl' => '/blarp/seven/foo.tpl',
'/../baz/foo.tpl' => '/baz/foo.tpl',
);
foreach ($paths as $path => $expected) {
// resolve relative path
if ($path[0] != '/') {
$_path = rtrim($cwd, '/') . '/' . ltrim($path, '/');
} else {
$_path = $path;
}
// resolve selfs
$_path = str_replace('/./', '/', $_path);
// resolve parents
while (true) {
$_parent = strpos($_path, '/../');
if ($_parent === false) {
break;
} else if ($_parent === 0) {
$_path = substr($_path, 3);
break;
}
$_pos = strrpos($_path, '/', $_parent - strlen($_path) - 1);
$_path = substr_replace($_path, '', $_pos, $_parent + 3 - $_pos);
}
echo $_path, " ", $_path == $expected ? '' : 'FAIL', "\n";
} |
is the fastest solution i could come up with in 15 minutes… (of course it would have to be extended to allow windows filepaths as well…) |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Tue Feb 01, 2011 8:46 pm Post subject: |
|
|
It's more lightweight variant:
Code: |
$cwd = '/blarp/bloo';
$paths = array(
'/foo/bar/../one/foo.tpl' => '/foo/one/foo.tpl',
'/foo/bar/../../two/foo.tpl' => '/two/foo.tpl',
'/foo/bar/.././three/foo.tpl' => '/foo/three/foo.tpl',
'/foo/bar/.././../four/foo.tpl' => '/four/foo.tpl',
'/foo/bar/../bla/../five/foo.tpl' => '/foo/five/foo.tpl',
'./six/foo.tpl' => '/blarp/bloo/six/foo.tpl',
'../seven/foo.tpl' => '/blarp/seven/foo.tpl',
'/../baz/foo.tpl' => '/baz/foo.tpl',
);
foreach ($paths as $path => $expected)
{
if (substr($path, 0, 1)!='/') $path=$cwd.'/'.$path;
$path=explode('/', $path);
$new_path=array();
$left=array_shift($path);
while($first=array_shift($path))
{
if ($first=='.') continue;
if ($first=='..')
{
array_pop($new_path);
continue;
}
$new_path[]=$first;
}
$path=$left.'/'.implode('/', $new_path);
echo $path, " ", $path == $expected ? '' : 'FAIL', "\n";
}
|
Last edited by Loki on Tue Feb 01, 2011 9:23 pm; edited 2 times in total |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Feb 01, 2011 8:57 pm Post subject: |
|
|
Loki wrote: | It's more lightweight variant: |
The "more lightweight" variant is even slower than your regex solution. Those array operations don't come cheap…
Also your approaches fail tests like /foo/bar/.././../four/foo.tpl because you're not removing /./ before you resolve /../ |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Tue Feb 01, 2011 9:21 pm Post subject: |
|
|
You not right - my "lightweight" variant up to x1.5 times faster than improved preg. But your variant up to x2 times faster than my "lightweight"
Quote: | you're not removing /./ before you resolve /../ |
I'm remove it here:
Code: | if ($first=='.') continue; |
|
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Tue Feb 01, 2011 10:15 pm Post subject: |
|
|
I check difference between file_exists and realpath (under ubuntu):
Code: |
$path=dirname(__FILE__)."/data/../index.php";
$start=microtime(true);
for($i=0; $i<10000; $i++)
{
if ($newPath=realpath($path)) true;
}
echo sprintf("%01.3f sec.", microtime(true)-$start);
$start=microtime(true);
for($i=0; $i<10000; $i++)
{
if (file_exists($path)) true;
}
echo sprintf("%01.3f sec.", microtime(true)-$start);
|
if file exists:
Code: |
0.096 sec.
0.128 sec. |
if file not exists:
Code: |
0.105 sec.
0.091 sec.
|
so it's possible to use realpath() instead of file_exists() without loss of performance. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Feb 01, 2011 10:20 pm Post subject: |
|
|
you can't use realpath() here. realpath() returns false if the given filepath does not exist. which would happen every time you're determining the compiled filepath of a not yet compiled template. in other words: introduce realpath() and you won't be able to save a single newly compiled template. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Feb 01, 2011 10:37 pm Post subject: |
|
|
Code: | define('DS', DIRECTORY_SEPARATOR);
//define('DS', "\\");
$cwd = '/blarp/bloo';
$paths = array(
'/foo/bar/../one/foo.tpl' => '/foo/one/foo.tpl',
'/foo/bar/../../two/foo.tpl' => '/two/foo.tpl',
'/foo/bar/.././three/foo.tpl' => '/foo/three/foo.tpl',
'/foo/bar/.././../four/foo.tpl' => '/four/foo.tpl',
'/foo/bar/../bla/../five/foo.tpl' => '/foo/five/foo.tpl',
'./six/foo.tpl' => '/blarp/bloo/six/foo.tpl',
'../seven/foo.tpl' => '/blarp/seven/foo.tpl',
'/foo/bar/../bla/..//nine//foo.tpl' => '/foo/nine/foo.tpl',
'//foo/bar/../bla/..//ten///foo.tpl' => '/foo/ten/foo.tpl',
'/foo/bar/../././../eleven/foo.tpl' => '/eleven/foo.tpl',
'/../baz/foo.tpl' => '/baz/foo.tpl',
'C:/baz/../foo.tpl' => "C:\\foo.tpl",
"C:/baz\\../foo.tpl" => "C:\\foo.tpl",
"C:/../baz\\../foo.tpl" => "C:\\foo.tpl",
);
foreach ($paths as $path => $expected) {
// resolve relative path
if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $path)) {
$_path = rtrim($cwd, '/\\') . DS . ltrim($path, '/\\');
} else {
$_path = $path;
}
// don't we all just love windows?
$_path = str_replace( '\\', '/', $_path );
// resolve simples
$_path = preg_replace('#(/\./(\./)*)|/{2,}#', '/', $_path);
// resolve parents
while (true) {
$_parent = strpos($_path, '/../');
if ($_parent === false) {
break;
} else if ($_parent === 0) {
$_path = substr($_path, 3);
break;
}
$_pos = strrpos($_path, '/', $_parent - strlen($_path) -1);
if ($_pos === false) {
// don't we all just love windows?
$_pos = $_parent;
}
$_path = substr_replace($_path, '', $_pos, $_parent + 3 - $_pos);
}
if (DS != '/') {
// don't we all just love windows?
$_path = str_replace( '/', '\\', $_path );
}
// this is only required for directories
$_path = rtrim($_path, '/\\');
echo $_path, " ", $_path == $expected ? '' : 'FAIL', "\n";
} |
should be a windows compatible solution |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Wed Feb 02, 2011 6:25 am Post subject: |
|
|
globe wrote: | you can't use realpath() here. realpath() returns false if the given filepath does not exist. which would happen every time you're determining the compiled filepath of a not yet compiled template. |
file_exists() checks path to template (not to compiled version)
look at method buildTemplateFilepath in sysplugins/smarty_internal_template.php
It uses file_exists() while generates path to template.
Therefore, I propose to replace the file_exists() by filepath() to bring paths to the unified view. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Wed Feb 02, 2011 8:21 am Post subject: |
|
|
Loki wrote: | I check difference between file_exists and realpath (under ubuntu): |
Your test is not clearing the stat cache, which is why your results are wrong…
Code: | $path=dirname(__FILE__)."/test.php";
$start=microtime(true);
for($i=0; $i<10000; $i++)
{
clearstatcache(true);
realpath($path);
}
echo sprintf("%01.3f sec.", microtime(true)-$start);
$start=microtime(true);
for($i=0; $i<10000; $i++)
{
clearstatcache(true);
file_exists($path);
}
echo sprintf("%01.3f sec.", microtime(true)-$start); |
For the existing file:
0.212 sec. (realpath)
0.040 sec. (file_exists)
For the not existing file:
0.048 sec. (realpath)
0.040 sec. (file_exists) |
|
Back to top |
|
U.Tews Administrator
Joined: 22 Nov 2006 Posts: 5068 Location: Hamburg / Germany
|
Posted: Wed Feb 02, 2011 8:46 am Post subject: |
|
|
Why not simply use a unique way of specifying the template_dir filepathes in your project.....
Also realpath() seems to behave exactly the same on non exiting files between different PHP 5 version like 5.2.4 include in Ubuntu.... |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Wed Feb 02, 2011 8:46 am Post subject: |
|
|
In the method buildTemplateFilepath already present verification that the path relative. So that verification can be simplified. |
|
Back to top |
|
Loki Smarty Rookie
Joined: 13 Jan 2011 Posts: 30
|
Posted: Wed Feb 02, 2011 8:58 am Post subject: |
|
|
U.Tews wrote: | Why not simply use a unique way of specifying the template_dir filepathes in your project..... |
In the code, I prefer to use absolute paths, so there's no problem - I can bring them to the required form. But in the templates are often more useful relative. For example, use a template from another module (../other_module) or other skin (../../other_skin/other_module). In these cases, I can not control in what form will the final path. |
|
Back to top |
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|
|