private function searchReferences($searchPath, $flags = 0)
{
$result = array();
$foundMatchingMappings = false;
$searchPath = rtrim($searchPath, '/');
$searchPathForTest = $searchPath . '/';
foreach ($this->json as $currentPath => $currentReferences) {
$currentPathForTest = rtrim($currentPath, '/') . '/';
// We found a mapping that matches the search path
// e.g. mapping /a/b for path /a/b
if ($searchPathForTest === $currentPathForTest) {
$foundMatchingMappings = true;
$currentReferences = $this->resolveReferences($currentPath, $currentReferences, $flags);
if (empty($currentReferences)) {
continue;
}
$result[$currentPath] = $currentReferences;
// Return unless an explicit mapping order is defined
// In that case, the ancestors need to be searched as well
if ($flags & self::STOP_ON_FIRST && !isset($this->json['_order'][$currentPath])) {
return $result;
}
continue;
}
// We found a mapping that lies within the search path
// e.g. mapping /a/b/c for path /a/b
if ($flags & self::INCLUDE_NESTED && 0 === strpos($currentPathForTest, $searchPathForTest)) {
$foundMatchingMappings = true;
$currentReferences = $this->resolveReferences($currentPath, $currentReferences, $flags);
if (empty($currentReferences)) {
continue;
}
$result[$currentPath] = $currentReferences;
// Return unless an explicit mapping order is defined
// In that case, the ancestors need to be searched as well
if ($flags & self::STOP_ON_FIRST && !isset($this->json['_order'][$currentPath])) {
return $result;
}
continue;
}
// We found a mapping that is an ancestor of the search path
// e.g. mapping /a for path /a/b
if (0 === strpos($searchPathForTest, $currentPathForTest)) {
$foundMatchingMappings = true;
if ($flags & self::INCLUDE_ANCESTORS) {
// Include the references of the ancestor
$currentReferences = $this->resolveReferences($currentPath, $currentReferences, $flags);
if (empty($currentReferences)) {
continue;
}
$result[$currentPath] = $currentReferences;
// Return unless an explicit mapping order is defined
// In that case, the ancestors need to be searched as well
if ($flags & self::STOP_ON_FIRST && !isset($this->json['_order'][$currentPath])) {
return $result;
}
continue;
}
if ($flags & self::NO_SEARCH_FILESYSTEM) {
continue;
}
// Check the filesystem directories pointed to by the ancestors
// for the searched path
$nestedPath = substr($searchPath, strlen($currentPathForTest));
$currentPathWithNested = rtrim($currentPath, '/') . '/' . $nestedPath;
// Follow links so that we can check the nested directories in
// the final transitive link targets
$currentReferencesResolved = $this->followLinks($this->resolveReferences($currentPath, $currentReferences, $flags & ~self::STOP_ON_FIRST));
// Append the path and check which of the resulting paths exist
$nestedReferences = $this->appendPathAndFilterExisting($currentReferencesResolved, $nestedPath, $flags);
// None of the results exists
if (empty($nestedReferences)) {
continue;
}
// Return unless an explicit mapping order is defined
// In that case, the ancestors need to be searched as well
if ($flags & self::STOP_ON_FIRST && !isset($this->json['_order'][$currentPathWithNested])) {
// The nested references already have size 1
return array($currentPathWithNested => $nestedReferences);
}
// We are traversing long keys before short keys
// It could be that this entry already exists.
if (!isset($result[$currentPathWithNested])) {
$result[$currentPathWithNested] = $nestedReferences;
continue;
}
// If no explicit mapping order is defined, simply append the
// new references to the existing ones
if (!isset($this->json['_order'][$currentPathWithNested])) {
$result[$currentPathWithNested] = array_merge($result[$currentPathWithNested], $nestedReferences);
continue;
}
// If an explicit mapping order is defined, store the paths
// of the mappings that generated each reference set and
// resolve the order later on
if (!isset($result[$currentPathWithNested][$currentPathWithNested])) {
$result[$currentPathWithNested] = array($currentPathWithNested => $result[$currentPathWithNested]);
}
// Add the new references generated by the current mapping
$result[$currentPathWithNested][$currentPath] = $nestedReferences;
continue;
}
// We did not find anything but previously found mappings
// The mappings are sorted alphabetically, so we can safely abort
if ($foundMatchingMappings) {
break;
}
}
// Resolve the order where it is explicitly set
if (!isset($this->json['_order'])) {
return $result;
}
foreach ($result as $currentPath => $referencesByMappedPath) {
// If no order is defined for the path or if only one mapped path
// generated references, there's nothing to do
if (!isset($this->json['_order'][$currentPath]) || !isset($referencesByMappedPath[$currentPath])) {
continue;
}
$orderedReferences = array();
foreach ($this->json['_order'][$currentPath] as $orderEntry) {
if (!isset($referencesByMappedPath[$orderEntry['path']])) {
continue;
}
for ($i = 0; $i < $orderEntry['references'] && count($referencesByMappedPath[$orderEntry['path']]) > 0; ++$i) {
$orderedReferences[] = array_shift($referencesByMappedPath[$orderEntry['path']]);
}
// Only include references of the first mapped path
// Since $stopOnFirst is set, those references have a
// maximum size of 1
if ($flags & self::STOP_ON_FIRST) {
break;
}
}
$result[$currentPath] = $orderedReferences;
}
return $result;
}