1: <?php
2: /**
3: * Smarty write file plugin
4: *
5: * @package Smarty
6: * @subpackage PluginsInternal
7: * @author Monte Ohrt
8: */
9:
10: /**
11: * Smarty Internal Write File Class
12: *
13: * @package Smarty
14: * @subpackage PluginsInternal
15: */
16: class Smarty_Internal_Runtime_WriteFile
17: {
18: /**
19: * Writes file in a safe way to disk
20: *
21: * @param string $_filepath complete filepath
22: * @param string $_contents file content
23: * @param Smarty $smarty smarty instance
24: *
25: * @throws SmartyException
26: * @return boolean true
27: */
28: public function writeFile($_filepath, $_contents, Smarty $smarty)
29: {
30: $_error_reporting = error_reporting();
31: error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING);
32: $_file_perms = property_exists($smarty, '_file_perms') ? $smarty->_file_perms : 0644;
33: $_dir_perms =
34: property_exists($smarty, '_dir_perms') ? (isset($smarty->_dir_perms) ? $smarty->_dir_perms : 0777) : 0771;
35: if ($_file_perms !== null) {
36: $old_umask = umask(0);
37: }
38: $_dirpath = dirname($_filepath);
39: // if subdirs, create dir structure
40: if ($_dirpath !== '.') {
41: $i = 0;
42: // loop if concurrency problem occurs
43: // see https://bugs.php.net/bug.php?id=35326
44: while (!is_dir($_dirpath)) {
45: if (@mkdir($_dirpath, $_dir_perms, true)) {
46: break;
47: }
48: clearstatcache();
49: if (++$i === 3) {
50: error_reporting($_error_reporting);
51: throw new SmartyException("unable to create directory {$_dirpath}");
52: }
53: sleep(1);
54: }
55: }
56: // write to tmp file, then move to overt file lock race condition
57: $_tmp_file = $_dirpath . DIRECTORY_SEPARATOR . str_replace(array('.', ','), '_', uniqid('wrt', true));
58: if (!file_put_contents($_tmp_file, $_contents)) {
59: error_reporting($_error_reporting);
60: throw new SmartyException("unable to write file {$_tmp_file}");
61: }
62: /*
63: * Windows' rename() fails if the destination exists,
64: * Linux' rename() properly handles the overwrite.
65: * Simply unlink()ing a file might cause other processes
66: * currently reading that file to fail, but linux' rename()
67: * seems to be smart enough to handle that for us.
68: */
69: if (Smarty::$_IS_WINDOWS) {
70: // remove original file
71: if (is_file($_filepath)) {
72: @unlink($_filepath);
73: }
74: // rename tmp file
75: $success = @rename($_tmp_file, $_filepath);
76: } else {
77: // rename tmp file
78: $success = @rename($_tmp_file, $_filepath);
79: if (!$success) {
80: // remove original file
81: if (is_file($_filepath)) {
82: @unlink($_filepath);
83: }
84: // rename tmp file
85: $success = @rename($_tmp_file, $_filepath);
86: }
87: }
88: if (!$success) {
89: error_reporting($_error_reporting);
90: throw new SmartyException("unable to write file {$_filepath}");
91: }
92: if ($_file_perms !== null) {
93: // set file permissions
94: chmod($_filepath, $_file_perms);
95: umask($old_umask);
96: }
97: error_reporting($_error_reporting);
98: return true;
99: }
100: }
101: