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