/** * Search for deleted properties and use the editor to delete these entries * * @param Config $oldconfig The config representing the state before the change * @param Config $newconfig The config representing the state after the change * @param Document $doc * * @throws ProgrammingError */ protected function diffPropertyDeletions(Config $oldconfig, Config $newconfig, Document $doc) { // Iterate over all properties in the old configuration file and remove those that don't // exist in the new config foreach ($oldconfig->toArray() as $section => $directives) { if (!is_array($directives)) { Logger::warning('Section-less property ' . (string) $directives . ' was ignored.'); continue; } if ($newconfig->hasSection($section)) { $newSection = $newconfig->getSection($section); $oldDomSection = $doc->getSection($section); foreach ($directives as $key => $value) { if ($value instanceof ConfigObject) { throw new ProgrammingError('Cannot diff recursive configs'); } if (null === $newSection->get($key) && $oldDomSection->hasDirective($key)) { $oldDomSection->removeDirective($key); } } } else { $doc->removeSection($section); } } }
/** * Read the ini file contained in a string and return a mutable DOM that can be used * to change the content of an INI file. * * @param $str A string containing the whole ini file * * @return Document The mutable DOM object. * @throws ConfigurationError In case the file is not parseable */ public static function parseIni($str) { $doc = new Document(); $sec = null; $dir = null; $coms = array(); $state = self::LINE_START; $escaping = null; $token = ''; $line = 0; for ($i = 0; $i < strlen($str); $i++) { $s = $str[$i]; switch ($state) { case self::LINE_START: if (ctype_space($s)) { continue; } switch ($s) { case '[': $state = self::SECTION; break; case ';': $state = self::COMMENT; break; default: $state = self::DIRECTIVE_KEY; $token = $s; break; } break; case self::ESCAPE: $token .= $s; $state = $escaping; $escaping = null; break; case self::SECTION: if ($s === "\n") { self::throwParseError('Unterminated SECTION', $line); } elseif ($s === '\\') { $state = self::ESCAPE; $escaping = self::SECTION; } elseif ($s !== ']') { $token .= $s; } else { $sec = new Section($token); $sec->setCommentsPre($coms); $doc->addSection($sec); $dir = null; $coms = array(); $state = self::LINE_END; $token = ''; } break; case self::DIRECTIVE_KEY: if ($s !== '=') { $token .= $s; } else { $dir = new Directive($token); $dir->setCommentsPre($coms); if (isset($sec)) { $sec->addDirective($dir); } else { Logger::warning(sprintf('Ini parser warning: section-less directive "%s" ignored. (l. %d)', $token, $line)); } $coms = array(); $state = self::DIRECTIVE_VALUE_START; $token = ''; } break; case self::DIRECTIVE_VALUE_START: if (ctype_space($s)) { continue; } elseif ($s === '"') { $state = self::DIRECTIVE_VALUE_QUOTED; } else { $state = self::DIRECTIVE_VALUE; $token = $s; } break; case self::DIRECTIVE_VALUE: /* Escaping non-quoted values is not supported by php_parse_ini, it might be reasonable to include in case we are switching completely our own parser implementation */ if ($s === "\n" || $s === ";") { $dir->setValue($token); $token = ''; if ($s === "\n") { $state = self::LINE_START; $line++; } elseif ($s === ';') { $state = self::COMMENT; } } else { $token .= $s; } break; case self::DIRECTIVE_VALUE_QUOTED: if ($s === '\\') { $state = self::ESCAPE; $escaping = self::DIRECTIVE_VALUE_QUOTED; } elseif ($s !== '"') { $token .= $s; } else { $dir->setValue($token); $token = ''; $state = self::LINE_END; } break; case self::COMMENT: case self::COMMENT_END: if ($s !== "\n") { $token .= $s; } else { $com = new Comment(); $com->setContent($token); $token = ''; // Comments at the line end belong to the current line's directive or section. Comments // on empty lines belong to the next directive that shows up. if ($state === self::COMMENT_END) { if (isset($dir)) { $dir->setCommentPost($com); } else { $sec->setCommentPost($com); } } else { $coms[] = $com; } $state = self::LINE_START; $line++; } break; case self::LINE_END: if ($s === "\n") { $state = self::LINE_START; $line++; } elseif ($s === ';') { $state = self::COMMENT_END; } break; } } // process the last token switch ($state) { case self::COMMENT: case self::COMMENT_END: $com = new Comment(); $com->setContent($token); if ($state === self::COMMENT_END) { if (isset($dir)) { $dir->setCommentPost($com); } else { $sec->setCommentPost($com); } } else { $coms[] = $com; } break; case self::DIRECTIVE_VALUE: $dir->setValue($token); $sec->addDirective($dir); break; case self::ESCAPE: case self::DIRECTIVE_VALUE_QUOTED: case self::DIRECTIVE_KEY: case self::SECTION: self::throwParseError('File ended in unterminated state ' . $state, $line); } if (!empty($coms)) { $doc->setCommentsDangling($coms); } return $doc; }