importFile($import_file); } } /** Layers configuration data from a file into an existing object * * The file must be a PHP script which return an array with keys that match the properties of the Conf class. Malformed files will throw an exception; unknown keys are silently ignored. Files may be imported is succession, though this is not currently used. * @param string $file Full path and file name for the file to import */ public function importFile(string $file): self { if (!file_exists($file)) { throw new Conf\Exception("fileMissing", $file); } elseif (!is_readable($file)) { throw new Conf\Exception("fileUnreadable", $file); } try { ob_start(); $arr = (@include $file); } catch (\Throwable $e) { $arr = null; } finally { ob_end_clean(); } if (!is_array($arr)) { throw new Conf\Exception("fileCorrupt", $file); } return $this->import($arr); } /** Layers configuration data from an associative array into an existing object * * The input array must have keys that match the properties of the Conf class; unknown keys are silently ignored. Arrays may be imported is succession, though this is not currently used. * @param mixed[] $arr Array of configuration parameters to export */ public function import(array $arr): self { foreach ($arr as $key => $value) { $this->$key = $value; } return $this; } /** Outputs configuration settings, either non-default ones or all, as an associative array * @param bool $full Whether to output all configuration options rather than only changed ones */ public function export(bool $full = false): array { $ref = new self; $out = []; $conf = new \ReflectionObject($this); foreach ($conf->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { $name = $prop->name; // add the property to the output if the value is scalar (or null) and either: // 1. full output has been requested // 2. the property is not defined in the class // 3. it differs from the default if ((is_scalar($this->$name) || is_null($this->$name)) && ($full || !$prop->isDefault() || $this->$name !== $ref->$name)) { $out[$name] = $this->$name; } } return $out; } /** Outputs configuration settings, either non-default ones or all, to a file in a format suitable for later import * @param string $file Full path and file name for the file to import to; the containing directory must already exist * @param bool $full Whether to output all configuration options rather than only changed ones */ public function exportFile(string $file, bool $full = false): bool { $arr = $this->export($full); $conf = new \ReflectionObject($this); $out = " $value) { $match = null; $doc = $comment = ""; // retrieve the property's docblock, if it exists try { $doc = (new \ReflectionProperty(self::class, $prop))->getDocComment(); } catch (\ReflectionException $e) { } if ($doc) { // parse the docblock to extract the property description if (preg_match("<@var\s+\S+\s+(.+?)(?:\s*\*/)?$>m", $doc, $match)) { $comment = $match[1]; } } // append the docblock description if there is one, or an empty comment otherwise $out .= " // ".$comment.PHP_EOL; // append the property and an export of its value to the output $out .= " ".var_export($prop, true)." => ".var_export($value, true).",".PHP_EOL; } $out .= "];".PHP_EOL; // write the configuration representation to the requested file if (!@file_put_contents($file, $out)) { // if it fails throw an exception $err = file_exists($file) ? "fileUnwritable" : "fileUncreatable"; throw new Conf\Exception($err, $file); } return true; } }