Smarty Forum Index Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon.

Problem with compile filepath
Goto page 1, 2, 3  Next
 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Smarty Development
View previous topic :: View next topic  
Author Message
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Feb 01, 2011 5:13 pm    Post subject: Problem with compile filepath Reply with quote

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
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Tue Feb 01, 2011 6:46 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Feb 01, 2011 7:49 pm    Post subject: Reply with quote

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
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Tue Feb 01, 2011 8:43 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Feb 01, 2011 8:46 pm    Post subject: Reply with quote

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
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Tue Feb 01, 2011 8:57 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Feb 01, 2011 9:21 pm    Post subject: Reply with quote

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"Smile

Quote:
you're not removing /./ before you resolve /../

I'm remove it here:
Code:
if ($first=='.') continue;
Back to top
View user's profile Send private message
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Feb 01, 2011 10:15 pm    Post subject: Reply with quote

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
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Tue Feb 01, 2011 10:20 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Tue Feb 01, 2011 10:37 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Wed Feb 02, 2011 6:25 am    Post subject: Reply with quote

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
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Wed Feb 02, 2011 8:21 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
U.Tews
Administrator


Joined: 22 Nov 2006
Posts: 5068
Location: Hamburg / Germany

PostPosted: Wed Feb 02, 2011 8:46 am    Post subject: Reply with quote

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
View user's profile Send private message
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Wed Feb 02, 2011 8:46 am    Post subject: Reply with quote

In the method buildTemplateFilepath already present verification that the path relative. So that verification can be simplified.
Back to top
View user's profile Send private message
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Wed Feb 02, 2011 8:58 am    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Smarty Development All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
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
Protected by Anti-Spam ACP