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