xref: /dokuwiki/lib/plugins/config/core/Writer.php (revision 90fb952c4c30c09c8446076ba05991c89a3f0b01)
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        opcache_invalidate($file);
93        restore_error_handler();
94    }
95
96    /**
97     * Configuration is considered locked if there is no local settings filename
98     * or the directory its in is not writable or the file exists and is not writable
99     *
100     * @return bool true: locked, false: writable
101     */
102    public function isLocked()
103    {
104        if (!$this->savefile) return true;
105        if (!is_writable(dirname($this->savefile))) return true;
106        if (file_exists($this->savefile) && !is_writable($this->savefile)) return true;
107        return false;
108    }
109
110    /**
111     * Returns the PHP intro header for the config file
112     *
113     * @return string
114     */
115    protected function getHeader()
116    {
117        return implode(
118            "\n",
119            [
120                '<?php',
121                '/*',
122                ' * ' . $this->header,
123                ' * Auto-generated by config plugin',
124                ' * Run for user: ' . ($_SERVER['REMOTE_USER'] ?? 'Unknown'),
125                ' * Date: ' . date('r'),
126                ' */',
127                '',
128                ''
129            ]
130        );
131    }
132}
133