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", [ 'header, ' * Auto-generated by config plugin', ' * Run for user: ' . ($_SERVER['REMOTE_USER'] ?? 'Unknown'), ' * Date: ' . date('r'), ' */', '', '' ] ); } }