Puli\Repository\JsonRepository::searchReferences PHP Method

searchReferences() private method

The JSON is scanned starting with the longest mapped Puli path. If the search path is "/a/b", the result includes: * The references of the mapped path "/a/b". If the flag INCLUDE_ANCESTORS is used, the result additionally includes: * The references of any mapped super path "/a" with the sub-path "/b" appended. If the flag INCLUDE_NESTED is used, the result additionally includes: * The references of any mapped sub path "/a/b/c". This is useful if you want to look for the children of "/a/b" or scan all descendants for paths matching a given pattern. The result of this method is an array with two levels: * The first level has Puli paths as keys. * The second level contains all references for that path, where the first reference has the highest, the last reference the lowest priority. The keys of the second level are integers. There may be holes between any two keys. The references of the second level contain: * null values for virtual resources * strings starting with "@" for links * absolute filesystem paths for filesystem resources The flag STOP_ON_FIRST may be used to stop the search at the first result. The flag NO_SEARCH_FILESYSTEM may be used to check for whether the found paths actually exist on the filesystem.
private searchReferences ( string $searchPath, integer $flags ) : array
$searchPath string The path to search.
$flags integer A bitwise combination of the flag constants in this class.
return array An array with two levels.
    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;
    }