xref: /dokuwiki/lib/plugins/config/core/Writer.php (revision d4f83172d9533c4d84f450fe22ef630816b21d75)
15a38a129SAndreas Gohr<?php
25a38a129SAndreas Gohr
35a38a129SAndreas Gohrnamespace dokuwiki\plugin\config\core;
4*d4f83172SAndreas Gohr
50a5b05ebSAndreas Gohruse dokuwiki\plugin\config\core\Setting\Setting;
65a5180beSAmmar Abdulhamiduse dokuwiki\Logger;
75a38a129SAndreas Gohr
85a38a129SAndreas Gohr/**
95a38a129SAndreas Gohr * Writes the settings to the correct local file
105a38a129SAndreas Gohr */
118c7c53b0SAndreas Gohrclass Writer
128c7c53b0SAndreas Gohr{
135a38a129SAndreas Gohr    /** @var string header info */
145a38a129SAndreas Gohr    protected $header = 'Dokuwiki\'s Main Configuration File - Local Settings';
155a38a129SAndreas Gohr
165a38a129SAndreas Gohr    /** @var string the file where the config will be saved to */
175a38a129SAndreas Gohr    protected $savefile;
185a38a129SAndreas Gohr
195a38a129SAndreas Gohr    /**
205a38a129SAndreas Gohr     * Writer constructor.
215a38a129SAndreas Gohr     */
22d868eb89SAndreas Gohr    public function __construct()
23d868eb89SAndreas Gohr    {
245a38a129SAndreas Gohr        global $config_cascade;
255a38a129SAndreas Gohr        $this->savefile = end($config_cascade['main']['local']);
265a38a129SAndreas Gohr    }
275a38a129SAndreas Gohr
285a38a129SAndreas Gohr    /**
295a38a129SAndreas Gohr     * Save the given settings
305a38a129SAndreas Gohr     *
315a38a129SAndreas Gohr     * @param Setting[] $settings
325a38a129SAndreas Gohr     * @throws \Exception
335a38a129SAndreas Gohr     */
34d868eb89SAndreas Gohr    public function save($settings)
35d868eb89SAndreas Gohr    {
365a38a129SAndreas Gohr        global $conf;
375a38a129SAndreas Gohr        if ($this->isLocked()) throw new \Exception('no save');
385a38a129SAndreas Gohr
395a38a129SAndreas Gohr        // backup current file (remove any existing backup)
405a38a129SAndreas Gohr        if (file_exists($this->savefile)) {
4149bcbaeeSAndreas Gohr            if (file_exists($this->savefile . '.bak.php')) @unlink($this->savefile . '.bak.php');
4249bcbaeeSAndreas Gohr            if (!io_rename($this->savefile, $this->savefile . '.bak.php')) throw new \Exception('no backup');
435a38a129SAndreas Gohr        }
445a38a129SAndreas Gohr
455a38a129SAndreas Gohr        if (!$fh = @fopen($this->savefile, 'wb')) {
4649bcbaeeSAndreas Gohr            io_rename($this->savefile . '.bak.php', $this->savefile); // problem opening, restore the backup
475a38a129SAndreas Gohr            throw new \Exception('no save');
485a38a129SAndreas Gohr        }
495a38a129SAndreas Gohr
500772dde2SAndreas Gohr        $out = '';
515a38a129SAndreas Gohr        foreach ($settings as $setting) {
52f00299d8SAndreas Gohr            if ($setting->shouldBeSaved()) {
535a38a129SAndreas Gohr                $out .= $setting->out('conf', 'php');
545a38a129SAndreas Gohr            }
55f00299d8SAndreas Gohr        }
565a38a129SAndreas Gohr
570772dde2SAndreas Gohr        if ($out === '') {
580772dde2SAndreas Gohr            throw new \Exception('empty config');
590772dde2SAndreas Gohr        }
600772dde2SAndreas Gohr        $out = $this->getHeader() . $out;
610772dde2SAndreas Gohr
625a38a129SAndreas Gohr        fwrite($fh, $out);
635a38a129SAndreas Gohr        fclose($fh);
645a38a129SAndreas Gohr        if ($conf['fperm']) chmod($this->savefile, $conf['fperm']);
6570b28bcfSAndreas Gohr        $this->opcacheUpdate($this->savefile);
665a38a129SAndreas Gohr    }
675a38a129SAndreas Gohr
685a38a129SAndreas Gohr    /**
695a38a129SAndreas Gohr     * Update last modified time stamp of the config file
705a38a129SAndreas Gohr     *
715a38a129SAndreas Gohr     * Will invalidate all DokuWiki caches
725a38a129SAndreas Gohr     *
735a38a129SAndreas Gohr     * @throws \Exception when the config isn't writable
745a38a129SAndreas Gohr     */
75d868eb89SAndreas Gohr    public function touch()
76d868eb89SAndreas Gohr    {
775a38a129SAndreas Gohr        if ($this->isLocked()) throw new \Exception('no save');
785a38a129SAndreas Gohr        @touch($this->savefile);
7970b28bcfSAndreas Gohr        $this->opcacheUpdate($this->savefile);
8070b28bcfSAndreas Gohr    }
8170b28bcfSAndreas Gohr
8270b28bcfSAndreas Gohr    /**
835a5180beSAmmar Abdulhamid     * Invalidate the opcache of the given file (if possible)
8470b28bcfSAndreas Gohr     *
8570b28bcfSAndreas Gohr     * @todo this should probably be moved to core
8670b28bcfSAndreas Gohr     * @param string $file
8770b28bcfSAndreas Gohr     */
88d868eb89SAndreas Gohr    protected function opcacheUpdate($file)
89d868eb89SAndreas Gohr    {
9070b28bcfSAndreas Gohr        if (!function_exists('opcache_invalidate')) return;
915a5180beSAmmar Abdulhamid        set_error_handler(function ($errNo, $errMsg) {
92*d4f83172SAndreas Gohr            Logger::debug('Unable to invalidate opcache: ' . $errMsg);
93*d4f83172SAndreas Gohr        });
945a5180beSAmmar Abdulhamid        opcache_invalidate($file);
955a5180beSAmmar Abdulhamid        restore_error_handler();
965a38a129SAndreas Gohr    }
975a38a129SAndreas Gohr
985a38a129SAndreas Gohr    /**
995a38a129SAndreas Gohr     * Configuration is considered locked if there is no local settings filename
1005a38a129SAndreas Gohr     * or the directory its in is not writable or the file exists and is not writable
1015a38a129SAndreas Gohr     *
1025a38a129SAndreas Gohr     * @return bool true: locked, false: writable
1035a38a129SAndreas Gohr     */
104d868eb89SAndreas Gohr    public function isLocked()
105d868eb89SAndreas Gohr    {
1065a38a129SAndreas Gohr        if (!$this->savefile) return true;
1075a38a129SAndreas Gohr        if (!is_writable(dirname($this->savefile))) return true;
1085a38a129SAndreas Gohr        if (file_exists($this->savefile) && !is_writable($this->savefile)) return true;
1095a38a129SAndreas Gohr        return false;
1105a38a129SAndreas Gohr    }
1115a38a129SAndreas Gohr
1125a38a129SAndreas Gohr    /**
1135a38a129SAndreas Gohr     * Returns the PHP intro header for the config file
1145a38a129SAndreas Gohr     *
1155a38a129SAndreas Gohr     * @return string
1165a38a129SAndreas Gohr     */
117d868eb89SAndreas Gohr    protected function getHeader()
118d868eb89SAndreas Gohr    {
119467c1427SAndreas Gohr        return implode(
1205a38a129SAndreas Gohr            "\n",
121467c1427SAndreas Gohr            [
1225a38a129SAndreas Gohr                '<?php',
1235a38a129SAndreas Gohr                '/*',
1245a38a129SAndreas Gohr                ' * ' . $this->header,
1255a38a129SAndreas Gohr                ' * Auto-generated by config plugin',
126467c1427SAndreas Gohr                ' * Run for user: ' . ($_SERVER['REMOTE_USER'] ?? 'Unknown'),
1275a38a129SAndreas Gohr                ' * Date: ' . date('r'),
1285a38a129SAndreas Gohr                ' */',
1295a38a129SAndreas Gohr                '',
1305a38a129SAndreas Gohr                ''
131467c1427SAndreas Gohr            ]
1325a38a129SAndreas Gohr        );
1335a38a129SAndreas Gohr    }
1345a38a129SAndreas Gohr}
135