/** * Transforms PHP primitives, arrays, objects etc. to INI. Complex structures like arrays and objects * can take multiple lines (every scalar value is on new line). * * @return array */ private static function parseSerializedString() { $type = self::$value[self::$index]; self::$index += 2; // <type>: switch ($type) { case 's': $length = intval(StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ':', self::$index))); self::$index += strlen($length) + 2; // :" $str = substr(self::$value, self::$index, $length); self::$index += strlen($str) + 2; // "; return ['type' => 'string', 'value' => $str]; case 'i': $number = StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ';', self::$index)); self::$index += strlen($number) + 1; // ; return ['type' => 'int', 'value' => intval($number)]; case 'd': $number = StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ';', self::$index)); self::$index += strlen($number) + 1; // ; return ['type' => 'double', 'value' => doubleval($number)]; case 'b': $strVal = StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ';', self::$index)); self::$index += 2; // <0|1>; return ['type' => 'boolean', 'value' => $strVal === '1']; case 'a': $length = intval(StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ':', self::$index))); self::$index += strlen($length) + 2; // :{ $subItems = []; for ($i = 0; $i < $length; $i++) { $key = self::parseSerializedString()['value']; $value = self::parseSerializedString(); $subItems[$key] = $value; } self::$index += 1; // } return ['type' => 'array', 'value' => $subItems]; case 'O': $classNameLength = intval(StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ':', self::$index))); self::$index += strlen($classNameLength) + 2; // :" $className = substr(self::$value, self::$index, $classNameLength); self::$index += $classNameLength + 2; // ": $attributeCount = intval(StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ':', self::$index))); self::$index += strlen($attributeCount) + 2; // :{ $attribute = []; for ($i = 0; $i < $attributeCount; $i++) { $attributeName = self::parseSerializedString()['value']; $attributeName = str_replace("*", '*', $attributeName); $attributeName = str_replace("{$className}", '-', $attributeName); $attributeValue = self::parseSerializedString(); $attribute[$attributeName] = $attributeValue; } self::$index += 1; // } return ['type' => 'object', 'class' => $className, 'value' => $attribute]; case 'N': return ['type' => 'null']; case 'r': case 'R': $number = StringUtils::substringFromTo(self::$value, self::$index, strpos(self::$value, ';', self::$index)); self::$index += strlen($number) + 1; // ; return ['type' => $type === 'r' ? '*pointer*' : '*reference*', 'value' => intval($number)]; default: return []; } }
/** * PHP (Zend, not HHVM) has a bug that causes parse_ini_string() to fail when the line * ends with an escaped quote, like: * * ``` * key = "start of some multiline value \" * continued here" * ``` * * The workaround is to replace CR and LF chars inside the values (and ONLY inside the values) * with custom placeholders which will then be reverted back. * * @param string $iniString * @return mixed */ private static function eolWorkaround_addPlaceholders($iniString) { $prefaceString = ' = "'; // sequence of characters before string value $position = 0; $length = strlen($iniString); $result = ""; // Read the string char by char while ($position < $length) { $nextPrefacePos = strpos($iniString, $prefaceString, $position); if ($nextPrefacePos === false) { // There are no more string values // Just append the rest of the string and we're done $result .= substr($iniString, $position); break; } // Append everything from the end of last string value to the start of another $result .= StringUtils::substringFromTo($iniString, $position, $nextPrefacePos + strlen($prefaceString)); // Set position to the start of the string value $position = $nextPrefacePos + strlen($prefaceString); $stringBeginPos = $stringEndPos = $position; $isEndOfString = false; while (!$isEndOfString) { if ($iniString[$position] === '\\') { // Found escaped character // Skip this one and the following one $position += 2; continue; } else { if ($iniString[$position] === '"') { // This is it. Unescaped double-quote means that the string value ends here. $isEndOfString = true; $stringEndPos = $position; } else { // Regular character. Boooring - move along. $position += 1; } } } // OK. We have the beginning and the end. Let's replace all line-endings with placeholders. $value = StringUtils::substringFromTo($iniString, $stringBeginPos, $stringEndPos); $result .= self::getReplacedEolString($value, 'charsToPlaceholders'); } return $result; }