/** * Recursively resolve Xrefs and compile data. * * @param Xref $xref * @param XrefTokenResolverCollection $tokenResolvers * @param string|null $includeType * @param string|array|null $includeTypeValue * @param array $xrefPath * @return array * * @throws CircularReferenceException * @throws Exception\AlreadyRegisteredException * @throws TreeCompiler\XrefResolver\Exception\UnknownXrefTypeException * @throws UnknownXrefException * @throws XrefResolverFormatException * @throws \Exception */ protected function recursiveCompileXref(Xref $xref, XrefTokenResolverCollection $tokenResolvers = null, $includeType = null, $includeTypeValue = null, &$xrefPath) { static $XREF_KEY = 0; static $XREF_RESOLVERS_KEY = 1; if (!isset($includeType)) { $includeType = static::INCLUDE_TYPE_GROUP; } switch ($includeType) { case static::INCLUDE_TYPE_GROUP: if (!isset($includeTypeValue)) { $includeTypeValue = $this->includeMainKey; } else { if (gettype($includeTypeValue) != 'string') { throw new XrefResolverFormatException($xref, sprintf("Include type value must be a string representing the include group name. " . "\"%s\" given instead.", gettype($includeTypeValue))); } } break; case static::INCLUDE_TYPE_XREF: if (!is_array($includeTypeValue) || empty($includeTypeValue)) { throw new XrefResolverFormatException($xref, "Include type value must be a non-empty array of strings for named includes."); } break; default: throw new \Exception(sprintf('Unknown include type "%s".', $includeType)); } $mustIncludeSpecificGroup = $includeTypeValue != $this->includeMainKey; $xref->resolve(); $xrefData = $xref->getData(); if (empty($xrefData)) { if (!is_array($xrefData)) { throw new XrefResolverFormatException($xref, "De-serialized data must be an array."); } return array(); } if (!isset($xrefData[$this->includeKey]) || !is_array($xrefData[$this->includeKey])) { switch ($includeType) { case static::INCLUDE_TYPE_XREF: throw new XrefResolverFormatException($xref, sprintf("Required to explicitly include the list of Xref keys [\"%s\"] " . "but the \"%s\" key is missing from the first level.", implode('", "', $includeTypeValue), $this->includeKey)); case static::INCLUDE_TYPE_GROUP: if (!$mustIncludeSpecificGroup) { if (isset($xrefData[$this->addKey])) { $result = $xrefData[$this->addKey]; } else { if (isset($xrefData[$this->removeKey])) { unset($xrefData[$this->removeKey]); } $result = $xrefData; } if (isset($tokenResolvers)) { $tokenResolvers->applyToArray($result); } return $result; } throw new XrefResolverFormatException($xref, sprintf("Required to explicitly include the \"%s\" group of Xref keys " . "but the \"%s\" key is missing from the first level.", $includeTypeValue, $this->includeKey)); } } $xrefDataInclude =& $xrefData[$this->includeKey]; if (!isset($xrefDataInclude[$this->includeXrefKey]) || !is_array($xrefDataInclude[$this->includeXrefKey])) { switch ($includeType) { case static::INCLUDE_TYPE_XREF: throw new XrefResolverFormatException($xref, sprintf("Required to explicitly include the list of Xref keys [\"%s\"] " . "but the \"%s\" key is missing from the \"%s\" key on the first level.", implode('", "', $includeTypeValue), $this->includeXrefKey, $this->includeKey)); case static::INCLUDE_TYPE_GROUP: if (!$mustIncludeSpecificGroup) { if (isset($xrefData[$this->addKey])) { $result = $xrefData[$this->addKey]; } else { if (isset($xrefData[$this->removeKey])) { unset($xrefData[$this->removeKey]); } $result = $xrefData; } if (isset($tokenResolvers)) { $tokenResolvers->applyToArray($result); } return $result; } throw new XrefResolverFormatException($xref, sprintf("Required to explicitly include the \"%s\" group of Xref keys " . "but the \"%s\" key is missing from the \"%s\" key on the first level.", $includeTypeValue, $this->includeXrefKey, $this->includeKey)); } } $xrefDataIncludeXrefs =& $xrefDataInclude[$this->includeXrefKey]; $xrefKeysToBeResolved = $this->getXrefKeysToBeResolved($xref, $xrefDataInclude, $xrefDataIncludeXrefs, $includeType, $includeTypeValue); $xrefsToBeParsed = array(); foreach ($xrefKeysToBeResolved as $xrefKeyToBeResolved) { if (!isset($xrefDataIncludeXrefs[$xrefKeyToBeResolved])) { throw new UnknownXrefException(sprintf("Unable to find the required Xref definition named \"%s\".", $xrefKeyToBeResolved)); } $xrefsToBeParsed[$xrefKeyToBeResolved] = $xrefDataIncludeXrefs[$xrefKeyToBeResolved]; } unset($xrefDataIncludeXrefs); $xrefId = $xref->getId(); $xrefPath[$xrefId] = $xref; $xrefsToBeResolved = array(); foreach ($xrefsToBeParsed as $xrefKey => $xrefInfo) { $includedXref = $this->parseXrefInfo($xrefKey, $xrefInfo, $tokenResolvers, $xrefPath); if (is_array($xrefInfo) && isset($xrefInfo[$this->includeXrefResolversKey])) { $xrefTokenResolvers = $this->parseXrefTokenResolverDefinitions($xrefKey, $xrefInfo[$this->includeXrefResolversKey], $tokenResolvers, $xrefPath); } else { $xrefTokenResolvers = null; } $xrefsToBeResolved[] = array($XREF_KEY => $includedXref, $XREF_RESOLVERS_KEY => $xrefTokenResolvers); } /** @var array $includedXrefs */ $result = array(); foreach ($xrefsToBeResolved as $xrefToBeResolved) { /** @var Xref $includedXref */ $includedXref = $xrefToBeResolved[$XREF_KEY]; if (isset($xrefPath[$includedXref->getId()])) { throw new CircularReferenceException(sprintf('Tree compiler encountered circular reference at "%s" in path ["%s"].', sprintf('%s:%s', $includedXref->getType(), $includedXref->getLocation()), implode('", "', $xrefPath))); } $this->xrefs->add($includedXref); /** @var XrefTokenResolverCollection $includeTokenResolvers */ $includeTokenResolvers = $xrefToBeResolved[$XREF_RESOLVERS_KEY]; if (isset($includeTokenResolvers)) { $downTokenResolvers = $includeTokenResolvers; } else { $downTokenResolvers = new XrefTokenResolverCollection(); } if (isset($tokenResolvers)) { $downTokenResolvers->addCollection($tokenResolvers); } $includeData = $this->recursiveCompileXref($includedXref, $downTokenResolvers, static::INCLUDE_TYPE_GROUP, $this->includeMainKey, $xrefPath); $this->recursiveAddData($includeData, $result); } unset($xrefPath[$xrefId]); if (isset($xrefData[$this->removeKey])) { if (isset($tokenResolvers)) { $tokenResolvers->applyToArray($xrefData[$this->removeKey]); } $this->recursiveRemoveData($xrefData[$this->removeKey], $result); } if (isset($xrefData[$this->addKey])) { if (isset($tokenResolvers)) { $tokenResolvers->applyToArray($xrefData[$this->addKey]); } $this->recursiveAddData($xrefData[$this->addKey], $result); } return $result; }