xref: /dokuwiki/lib/plugins/config/core/Writer.php (revision d868eb89f182718a31113373a6272670bd7f8012)
1<?php
2
3namespace dokuwiki\plugin\config\core;
4use dokuwiki\plugin\config\core\Setting\Setting;
5use dokuwiki\Logger;
6
7/**
8 * Writes the settings to the correct local file
9 */
10class Writer
11{
12    /** @var string header info */
13    protected $header = 'Dokuwiki\'s Main Configuration File - Local Settings';
14
15    /** @var string the file where the config will be saved to */
16    protected $savefile;
17
18    /**
19     * Writer constructor.
20     */
21    public function __construct()
22    {
23        global $config_cascade;
24        $this->savefile = end($config_cascade['main']['local']);
25    }
26
27    /**
28     * Save the given settings
29     *
30     * @param Setting[] $settings
31     * @throws \Exception
32     */
33    public function save($settings)
34    {
35        global $conf;
36        if($this->isLocked()) throw new \Exception('no save');
37
38        // backup current file (remove any existing backup)
39        if(file_exists($this->savefile)) {
40            if(file_exists($this->savefile . '.bak.php')) @unlink($this->savefile . '.bak.php');
41            if(!io_rename($this->savefile, $this->savefile . '.bak.php')) throw new \Exception('no backup');
42        }
43
44        if(!$fh = @fopen($this->savefile, 'wb')) {
45            io_rename($this->savefile . '.bak.php', $this->savefile); // problem opening, restore the backup
46            throw new \Exception('no save');
47        }
48
49        $out = '';
50        foreach($settings as $setting) {
51            if($setting->shouldBeSaved()) {
52                $out .= $setting->out('conf', 'php');
53            }
54        }
55
56        if($out === '') {
57            throw new \Exception('empty config');
58        }
59        $out = $this->getHeader() . $out;
60
61        fwrite($fh, $out);
62        fclose($fh);
63        if($conf['fperm']) chmod($this->savefile, $conf['fperm']);
64        $this->opcacheUpdate($this->savefile);
65    }
66
67    /**
68     * Update last modified time stamp of the config file
69     *
70     * Will invalidate all DokuWiki caches
71     *
72     * @throws \Exception when the config isn't writable
73     */
74    public function touch()
75    {
76        if($this->isLocked()) throw new \Exception('no save');
77        @touch($this->savefile);
78        $this->opcacheUpdate($this->savefile);
79    }
80
81    /**
82     * Invalidate the opcache of the given file (if possible)
83     *
84     * @todo this should probably be moved to core
85     * @param string $file
86     */
87    protected function opcacheUpdate($file)
88    {
89        if(!function_exists('opcache_invalidate')) return;
90        set_error_handler(function ($errNo, $errMsg) {
91            Logger::debug('Unable to invalidate opcache: ' . $errMsg); }
92        );
93        opcache_invalidate($file);
94        restore_error_handler();
95    }
96
97    /**
98     * Configuration is considered locked if there is no local settings filename
99     * or the directory its in is not writable or the file exists and is not writable
100     *
101     * @return bool true: locked, false: writable
102     */
103    public function isLocked()
104    {
105        if(!$this->savefile) return true;
106        if(!is_writable(dirname($this->savefile))) return true;
107        if(file_exists($this->savefile) && !is_writable($this->savefile)) return true;
108        return false;
109    }
110
111    /**
112     * Returns the PHP intro header for the config file
113     *
114     * @return string
115     */
116    protected function getHeader()
117    {
118        return implode(
119            "\n",
120            [
121                '<?php',
122                '/*',
123                ' * ' . $this->header,
124                ' * Auto-generated by config plugin',
125                ' * Run for user: ' . ($_SERVER['REMOTE_USER'] ?? 'Unknown'),
126                ' * Date: ' . date('r'),
127                ' */',
128                '',
129                ''
130            ]
131        );
132    }
133}
134