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