aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/lib/plugins/config/core/Writer.php
blob: 6751b88ba0fdceb97b6a81c5164587fecfd1acc9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?php

namespace dokuwiki\plugin\config\core;

use dokuwiki\plugin\config\core\Setting\Setting;
use dokuwiki\Logger;

/**
 * Writes the settings to the correct local file
 */
class Writer
{
    /** @var string header info */
    protected $header = 'Dokuwiki\'s Main Configuration File - Local Settings';

    /** @var string the file where the config will be saved to */
    protected $savefile;

    /**
     * Writer constructor.
     */
    public function __construct()
    {
        global $config_cascade;
        $this->savefile = end($config_cascade['main']['local']);
    }

    /**
     * Save the given settings
     *
     * @param Setting[] $settings
     * @throws \Exception
     */
    public function save($settings)
    {
        global $conf;
        if ($this->isLocked()) throw new \Exception('no save');

        // backup current file (remove any existing backup)
        if (file_exists($this->savefile)) {
            if (file_exists($this->savefile . '.bak.php')) @unlink($this->savefile . '.bak.php');
            if (!io_rename($this->savefile, $this->savefile . '.bak.php')) throw new \Exception('no backup');
        }

        if (!$fh = @fopen($this->savefile, 'wb')) {
            io_rename($this->savefile . '.bak.php', $this->savefile); // problem opening, restore the backup
            throw new \Exception('no save');
        }

        $out = '';
        foreach ($settings as $setting) {
            if ($setting->shouldBeSaved()) {
                $out .= $setting->out('conf', 'php');
            }
        }

        if ($out === '') {
            throw new \Exception('empty config');
        }
        $out = $this->getHeader() . $out;

        fwrite($fh, $out);
        fclose($fh);
        if ($conf['fperm']) chmod($this->savefile, $conf['fperm']);
        $this->opcacheUpdate($this->savefile);
    }

    /**
     * Update last modified time stamp of the config file
     *
     * Will invalidate all DokuWiki caches
     *
     * @throws \Exception when the config isn't writable
     */
    public function touch()
    {
        if ($this->isLocked()) throw new \Exception('no save');
        @touch($this->savefile);
        $this->opcacheUpdate($this->savefile);
    }

    /**
     * Invalidate the opcache of the given file (if possible)
     *
     * @todo this should probably be moved to core
     * @param string $file
     */
    protected function opcacheUpdate($file)
    {
        if (!function_exists('opcache_invalidate')) return;
        set_error_handler(function ($errNo, $errMsg) {
            Logger::debug('Unable to invalidate opcache: ' . $errMsg);
        });
        opcache_invalidate($file);
        restore_error_handler();
    }

    /**
     * Configuration is considered locked if there is no local settings filename
     * or the directory its in is not writable or the file exists and is not writable
     *
     * @return bool true: locked, false: writable
     */
    public function isLocked()
    {
        if (!$this->savefile) return true;
        if (!is_writable(dirname($this->savefile))) return true;
        if (file_exists($this->savefile) && !is_writable($this->savefile)) return true;
        return false;
    }

    /**
     * Returns the PHP intro header for the config file
     *
     * @return string
     */
    protected function getHeader()
    {
        return implode(
            "\n",
            [
                '<?php',
                '/*',
                ' * ' . $this->header,
                ' * Auto-generated by config plugin',
                ' * Run for user: ' . ($_SERVER['REMOTE_USER'] ?? 'Unknown'),
                ' * Date: ' . date('r'),
                ' */',
                '',
                ''
            ]
        );
    }
}