/** * Constructor * * @param string $id The context ID * @param Tree $tree Current node of the tree */ protected function __construct($id, Tree $tree) { $wcSteps = array(); $lastWcSeg = $lastWcKey = $segment = $wild = 0; $lastFragEnd = -1; $invalid = $tree->handler() === null ? true : false; $wcs = $tree->wildcards(); $this->tree = $tree; $this->id = (string) $id; if ($invalid && isset($wcs[0]) && $tree->startPosition() != $wcs[0]->startPosition()) { $lastWcKey = $wild; $lastWcSeg = $segment; $wcSteps[$lastWcKey][$lastWcSeg] = array(Consts::CONTEXT_ID => '', Consts::CONTEXT_TYPE => Consts::CONTEXT_TYPE_WILDCARD, Consts::CONTEXT_START => $tree->startPosition(), Consts::CONTEXT_END => $wcs[0]->startPosition(), Consts::CONTEXT_CONTEXT => null); ++$segment; } // See if there is anything in the wildcard foreach ($wcs as $wildKey => $wc) { $wcStart = $wc->startPosition(); // Combine the invalid tag fragment, make array smaller if ($invalid && isset($wcSteps[$lastWcKey][$lastWcSeg])) { if ($wcSteps[$lastWcKey][$lastWcSeg][Consts::CONTEXT_END] === $wcStart) { $wcStart = $wcSteps[$lastWcKey][$lastWcSeg][Consts::CONTEXT_START]; unset($wcSteps[$lastWcKey][$lastWcSeg]); } elseif ($lastFragEnd !== -1) { $wcStart = $lastFragEnd; } } $lastWcSeg = $segment; $lastWcKey = $wildKey; $wcEnd = $wc->endPosition(); $wcSteps[$lastWcKey][$lastWcSeg] = array(Consts::CONTEXT_ID => '', Consts::CONTEXT_TYPE => Consts::CONTEXT_TYPE_WILDCARD, Consts::CONTEXT_START => $wcStart, Consts::CONTEXT_END => -1, Consts::CONTEXT_CONTEXT => null); $tempChilds = $wc->childs(); // Is there any sub childs or not? if (empty($tempChilds)) { $wcSteps[$lastWcKey][$lastWcSeg][Consts::CONTEXT_END] = $wcEnd; ++$segment; continue; } // Get the start position of first child, it's where the first // wildcard ends $childStartPos = $tempChilds[0]->startPosition(); // If the wildcard's end share the same position with child start, // remove the wildcard if ($wcSteps[$lastWcKey][$lastWcSeg][Consts::CONTEXT_START] === $childStartPos) { unset($wcSteps[$lastWcKey][$lastWcSeg]); } else { $wcSteps[$lastWcKey][$lastWcSeg][Consts::CONTEXT_END] = $childStartPos; } // Picking sub childs? $lastChildEndPos = -1; foreach ($tempChilds as $childIdx => $child) { $childStartPos = $child->startPosition(); if ($lastChildEndPos !== $childStartPos && $lastChildEndPos !== -1) { $lastWcSeg = ++$segment; $wcSteps[$lastWcKey][$lastWcSeg] = array(Consts::CONTEXT_ID => '', Consts::CONTEXT_TYPE => Consts::CONTEXT_TYPE_WILDCARD, Consts::CONTEXT_START => $lastChildEndPos, Consts::CONTEXT_END => $childStartPos, Consts::CONTEXT_CONTEXT => null); } $lastChildEndPos = $child->endPosition() + 1; $wcSteps[$lastWcKey][++$segment] = array(Consts::CONTEXT_ID => $this->id . '/' . (string) $wildKey . '/' . (string) $childIdx, Consts::CONTEXT_TYPE => Consts::CONTEXT_TYPE_CHILD, Consts::CONTEXT_START => $childStartPos, Consts::CONTEXT_END => $lastChildEndPos, Consts::CONTEXT_CONTEXT => null); $lastFragEnd = $lastChildEndPos; } // No need to create wildcard for empty tail if ($lastChildEndPos >= $wcEnd) { ++$segment; continue; } $lastWcSeg = ++$segment; $wcSteps[$lastWcKey][$lastWcSeg] = array(Consts::CONTEXT_ID => '', Consts::CONTEXT_TYPE => Consts::CONTEXT_TYPE_WILDCARD, Consts::CONTEXT_START => $lastChildEndPos, Consts::CONTEXT_END => $wcEnd, Consts::CONTEXT_CONTEXT => null); $lastFragEnd = $wcEnd; ++$segment; } // Sort a little, make the step index continuous foreach ($wcSteps as $wcKey => $wcVal) { if (empty($wcVal)) { $this->steps[$wcKey] = array(); continue; } foreach ($wcVal as $step) { $this->steps[$wcKey][] = $step; } } }