@param Tree[]   $trees
@param string[] $fields
@param string[] $modifiers

@return Collection
@return Individual[]
768426
public function searchIndividualsAdvanced(array $trees, array $fields, array $modifiers): Collection
    {
        $fields = array_filter($fields);

        $query = DB::table('individuals')
            ->select(['individuals.*'])
            ->distinct();

        $this->whereTrees($query, 'i_file', $trees);

        // Join the following tables
        $father_name   = false;
        $mother_name   = false;
        $spouse_family = false;
        $indi_name     = false;
        $indi_date     = false;
        $fam_date      = false;
        $indi_plac     = false;
        $fam_plac      = false;

        foreach ($fields as $field_name => $field_value) {
            if ($field_value !== '') {
                if (substr($field_name, 0, 14) === 'FAMC:HUSB:NAME') {
                    $father_name = true;
                } elseif (substr($field_name, 0, 14) === 'FAMC:WIFE:NAME') {
                    $mother_name = true;
                } elseif (substr($field_name, 0, 4) === 'NAME') {
                    $indi_name = true;
                } elseif (strpos($field_name, ':DATE') !== false) {
                    if (substr($field_name, 0, 4) === 'FAMS') {
                        $fam_date      = true;
                        $spouse_family = true;
                    } else {
                        $indi_date = true;
                    }
                } elseif (strpos($field_name, ':PLAC') !== false) {
                    if (substr($field_name, 0, 4) === 'FAMS') {
                        $fam_plac      = true;
                        $spouse_family = true;
                    } else {
                        $indi_plac = true;
                    }
                } elseif ($field_name === 'FAMS:NOTE') {
                    $spouse_family = true;
                }
            }
        }

        if ($father_name || $mother_name) {
            $query->join('link AS l1', static function (JoinClause $join): void {
                $join
                    ->on('l1.l_file', '=', 'individuals.i_file')
                    ->on('l1.l_from', '=', 'individuals.i_id')
                    ->where('l1.l_type', '=', 'FAMC');
            });

            if ($father_name) {
                $query->join('link AS l2', static function (JoinClause $join): void {
                    $join
                        ->on('l2.l_file', '=', 'l1.l_file')
                        ->on('l2.l_from', '=', 'l1.l_to')
                        ->where('l2.l_type', '=', 'HUSB');
                });
                $query->join('name AS father_name', static function (JoinClause $join): void {
                    $join
                        ->on('father_name.n_file', '=', 'l2.l_file')
                        ->on('father_name.n_id', '=', 'l2.l_to');
                });
            }

            if ($mother_name) {
                $query->join('link AS l3', static function (JoinClause $join): void {
                    $join
                        ->on('l3.l_file', '=', 'l1.l_file')
                        ->on('l3.l_from', '=', 'l1.l_to')
                        ->where('l3.l_type', '=', 'WIFE');
                });
                $query->join('name AS mother_name', static function (JoinClause $join): void {
                    $join
                        ->on('mother_name.n_file', '=', 'l3.l_file')
                        ->on('mother_name.n_id', '=', 'l3.l_to');
                });
            }
        }

        if ($spouse_family) {
            $query->join('link AS l4', static function (JoinClause $join): void {
                $join
                    ->on('l4.l_file', '=', 'individuals.i_file')
                    ->on('l4.l_from', '=', 'individuals.i_id')
                    ->where('l4.l_type', '=', 'FAMS');
            });
            $query->join('families AS spouse_families', static function (JoinClause $join): void {
                $join
                    ->on('spouse_families.f_file', '=', 'l4.l_file')
                    ->on('spouse_families.f_id', '=', 'l4.l_to');
            });
        }

        if ($indi_name) {
            $query->join('name AS individual_name', static function (JoinClause $join): void {
                $join
                    ->on('individual_name.n_file', '=', 'individuals.i_file')
                    ->on('individual_name.n_id', '=', 'individuals.i_id');
            });
        }

        if ($indi_date) {
            $query->join('dates AS individual_dates', static function (JoinClause $join): void {
                $join
                    ->on('individual_dates.d_file', '=', 'individuals.i_file')
                    ->on('individual_dates.d_gid', '=', 'individuals.i_id');
            });
        }

        if ($fam_date) {
            $query->join('dates AS family_dates', static function (JoinClause $join): void {
                $join
                    ->on('family_dates.d_file', '=', 'spouse_families.f_file')
                    ->on('family_dates.d_gid', '=', 'spouse_families.f_id');
            });
        }

        if ($indi_plac) {
            $query->join('placelinks AS individual_placelinks', static function (JoinClause $join): void {
                $join
                    ->on('individual_placelinks.pl_file', '=', 'individuals.i_file')
                    ->on('individual_placelinks.pl_gid', '=', 'individuals.i_id');
            });
            $query->join('places AS individual_places', static function (JoinClause $join): void {
                $join
                    ->on('individual_places.p_file', '=', 'individual_placelinks.pl_file')
                    ->on('individual_places.p_id', '=', 'individual_placelinks.pl_p_id');
            });
        }

        if ($fam_plac) {
            $query->join('placelinks AS familyl_placelinks', static function (JoinClause $join): void {
                $join
                    ->on('familyl_placelinks.pl_file', '=', 'individuals.i_file')
                    ->on('familyl_placelinks.pl_gid', '=', 'individuals.i_id');
            });
            $query->join('places AS family_places', static function (JoinClause $join): void {
                $join
                    ->on('family_places.p_file', '=', 'familyl_placelinks.pl_file')
                    ->on('family_places.p_id', '=', 'familyl_placelinks.pl_p_id');
            });
        }

        foreach ($fields as $field_name => $field_value) {
            $parts = preg_split('/:/', $field_name . '::::');
            if ($parts[0] === 'NAME') {
                // NAME:*
                switch ($parts[1]) {
                    case 'GIVN':
                        switch ($modifiers[$field_name]) {
                            case 'EXACT':
                                $query->where('individual_name.n_givn', '=', $field_value);
                                break;
                            case 'BEGINS':
                                $query->where('individual_name.n_givn', 'LIKE', $field_value . '%');
                                break;
                            case 'CONTAINS':
                                $query->where('individual_name.n_givn', 'LIKE', '%' . $field_value . '%');
                                break;
                            case 'SDX_STD':
                                $sdx = Soundex::russell($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, 'individual_name.n_soundex_givn_std', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where('individual_name.n_givn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                            case 'SDX': // SDX uses DM by default.
                            case 'SDX_DM':
                                $sdx = Soundex::daitchMokotoff($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, 'individual_name.n_soundex_givn_dm', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where('individual_name.n_givn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                        }
                        break;
                    case 'SURN':
                        switch ($modifiers[$field_name]) {
                            case 'EXACT':
                                $query->where('individual_name.n_surn', '=', $field_value);
                                break;
                            case 'BEGINS':
                                $query->where('individual_name.n_surn', 'LIKE', $field_value . '%');
                                break;
                            case 'CONTAINS':
                                $query->where('individual_name.n_surn', 'LIKE', '%' . $field_value . '%');
                                break;
                            case 'SDX_STD':
                                $sdx = Soundex::russell($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, 'individual_name.n_soundex_surn_std', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where('individual_name.n_surn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                            case 'SDX': // SDX uses DM by default.
                            case 'SDX_DM':
                                $sdx = Soundex::daitchMokotoff($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, 'individual_name.n_soundex_surn_dm', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where('individual_name.n_surn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                        }
                        break;
                    case 'NICK':
                    case '_MARNM':
                    case '_HEB':
                    case '_AKA':
                        $query
                            ->where('individual_name', '=', $parts[1])
                            ->where('individual_name', 'LIKE', '%' . $field_value . '%');
                        break;
                }
                unset($fields[$field_name]);
            } elseif ($parts[1] === 'DATE') {
                // *:DATE
                $date = new Date($field_value);
                if ($date->isOK()) {
                    $delta = 365 * ($modifiers[$field_name] ?? 0);
                    $query
                        ->where('individual_dates.d_fact', '=', $parts[0])
                        ->where('individual_dates.d_julianday1', '>=', $date->minimumJulianDay() - $delta)
                        ->where('individual_dates.d_julianday2', '<=', $date->minimumJulianDay() + $delta);
                }
                unset($fields[$field_name]);
            } elseif ($parts[0] === 'FAMS' && $parts[2] === 'DATE') {
                // FAMS:*:DATE
                $date = new Date($field_value);
                if ($date->isOK()) {
                    $delta = 365 * $modifiers[$field_name];
                    $query
                        ->where('family_dates.d_fact', '=', $parts[1])
                        ->where('family_dates.d_julianday1', '>=', $date->minimumJulianDay() - $delta)
                        ->where('family_dates.d_julianday2', '<=', $date->minimumJulianDay() + $delta);
                }
                unset($fields[$field_name]);
            } elseif ($parts[1] === 'PLAC') {
                // *:PLAC
                // SQL can only link a place to a person/family, not to an event.
                $query->where('individual_places.p_place', 'LIKE', '%' . $field_value . '%');
            } elseif ($parts[0] === 'FAMS' && $parts[2] === 'PLAC') {
                // FAMS:*:PLAC
                // SQL can only link a place to a person/family, not to an event.
                $query->where('family_places.p_place', 'LIKE', '%' . $field_value . '%');
            } elseif ($parts[0] === 'FAMC' && $parts[2] === 'NAME') {
                $table = $parts[1] === 'HUSB' ? 'father_name' : 'mother_name';
                // NAME:*
                switch ($parts[3]) {
                    case 'GIVN':
                        switch ($modifiers[$field_name]) {
                            case 'EXACT':
                                $query->where($table . '.n_givn', '=', $field_value);
                                break;
                            case 'BEGINS':
                                $query->where($table . '.n_givn', 'LIKE', $field_value . '%');
                                break;
                            case 'CONTAINS':
                                $query->where($table . '.n_givn', 'LIKE', '%' . $field_value . '%');
                                break;
                            case 'SDX_STD':
                                $sdx = Soundex::russell($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, $table . '.n_soundex_givn_std', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where($table . '.n_givn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                            case 'SDX': // SDX uses DM by default.
                            case 'SDX_DM':
                                $sdx = Soundex::daitchMokotoff($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, $table . '.n_soundex_givn_dm', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where($table . '.n_givn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                        }
                        break;
                    case 'SURN':
                        switch ($modifiers[$field_name]) {
                            case 'EXACT':
                                $query->where($table . '.n_surn', '=', $field_value);
                                break;
                            case 'BEGINS':
                                $query->where($table . '.n_surn', 'LIKE', $field_value . '%');
                                break;
                            case 'CONTAINS':
                                $query->where($table . '.n_surn', 'LIKE', '%' . $field_value . '%');
                                break;
                            case 'SDX_STD':
                                $sdx = Soundex::russell($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, $table . '.n_soundex_surn_std', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where($table . '.n_surn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                            case 'SDX': // SDX uses DM by default.
                            case 'SDX_DM':
                                $sdx = Soundex::daitchMokotoff($field_value);
                                if ($sdx !== '') {
                                    $this->wherePhonetic($query, $table . '.n_soundex_surn_dm', $sdx);
                                } else {
                                    // No phonetic content? Use a substring match
                                    $query->where($table . '.n_surn', 'LIKE', '%' . $field_value . '%');
                                }
                                break;
                        }
                        break;
                }
                unset($fields[$field_name]);
            } elseif ($parts[0] === 'FAMS') {
                // e.g. searches for occupation, religion, note, etc.
                // Initial matching only.  Need PHP to apply filter.
                $query->where('families.f_gedcom', 'LIKE', "%\n1 " . $parts[1] . ' %' . $field_value . '%');
            } elseif ($parts[1] === 'TYPE') {
                // e.g. FACT:TYPE or EVEN:TYPE
                // Initial matching only.  Need PHP to apply filter.
                $query->where('individuals.i_gedcom', 'LIKE', "%\n1 " . $parts[0] . '%\n2 TYPE %' . $field_value . '%');
            } else {
                // e.g. searches for occupation, religion, note, etc.
                // Initial matching only.  Need PHP to apply filter.
                $query->where('individuals.i_gedcom', 'LIKE', "%\n1 " . $parts[0] . ' %' . $field_value . '%');
            }
        }

        return $query
            ->get()
            ->each($this->rowLimiter())
            ->map(Individual::rowMapper())
            ->filter(GedcomRecord::accessFilter())
            ->filter(static function (Individual $individual) use ($fields): bool {
                // Check for XXXX:PLAC fields, which were only partially matched by SQL
                foreach ($fields as $field_name => $field_value) {
                    $regex = '/' . preg_quote($field_value, '/') . '/i';

                    $parts = preg_split('/:/', $field_name . '::::');

                    if ($parts[1] === 'PLAC') {
                        // *:PLAC
                        foreach ($individual->facts([$parts[0]]) as $fact) {
                            if (preg_match($regex, $fact->place()->gedcomName())) {
                                return true;
                            }
                        }
                    } elseif ($parts[0] === 'FAMS' && $parts[2] === 'PLAC') {
                        // FAMS:*:PLAC
                        foreach ($individual->spouseFamilies() as $family) {
                            foreach ($family->facts([$parts[1]]) as $fact) {
                                if (preg_match($regex, $fact->place()->gedcomName())) {
                                    return true;
                                }
                            }
                        }
                    } elseif ($parts[0] === 'FAMS') {
                        // e.g. searches for occupation, religion, note, etc.
                        foreach ($individual->spouseFamilies() as $family) {
                            foreach ($family->facts([$parts[1]]) as $fact) {
                                if (preg_match($regex, $fact->value())) {
                                    return true;
                                }
                            }
                        }
                    } elseif ($parts[1] === 'TYPE') {
                        // e.g. FACT:TYPE or EVEN:TYPE
                        foreach ($individual->facts([$parts[0]]) as $fact) {
                            if (preg_match($regex, $fact->attribute('TYPE'))) {
                                return true;
                            }
                        }
                    } else {
                        // e.g. searches for occupation, religion, note, etc.
                        foreach ($individual->facts([$parts[0]]) as $fact) {
                            if (preg_match($regex, $fact->value())) {
                                return true;
                            }
                        }
                    }

                    // No match
                    return false;
                }

                return true;
            });
    }
1103245
private function parseModifiers($modifiers) {
		$info = new \n2n\log4php\formatting\FormattingInfo();
	
		// If no modifiers are given, return default values
		if (empty($modifiers)) {
			return $info;
		}
	
		// Validate
		$pattern = '/^(-?[0-9]+)?\.?-?[0-9]+$/';
		if (!preg_match($pattern, $modifiers)) {
			throw new \n2n\log4php\LoggerException("log4php: Invalid modifier in conversion pattern: [$modifiers]. Ignoring modifier.", E_USER_WARNING);
			return $info;
		}
	
		$parts = explode('.', $modifiers);
	
		if (!empty($parts[0])) {
			$minPart = (integer) $parts[0];
			$info->min = abs($minPart);
			$info->padLeft = ($minPart > 0);
		}
	
		if (!empty($parts[1])) {
			$maxPart = (integer) $parts[1];
			$info->max = abs($maxPart);
			$info->trimLeft = ($maxPart < 0);
		}
	
		return $info;
	}
566194
public static function fromArray(array $fields)
    {
        $form = new static();
        foreach ($fields as $key => $field) {
            $form->addField($field, $key);
        }

        return $form;
    }
828516
public function getIntersections($geoJson, array $modifiers = [], array $options = [], &$request = null) {
        return $this->geoQuery("intersects", $geoJson, $modifiers, $options, $request);
    }
1219129
public function create(RouteCollection $routes): Tree
    {
        $routesArray = $routes->all();

        $routesPaths = array_map(function (Route $route) {
            return $this->serializer->serialize($route->getPath());
        }, $routesArray);

        $tree = new Tree();

        /** @var RoutePath[] $pathArray */
        foreach ($routesPaths as $index => $pathArray) {
            $route = $routesArray[$index];

            /** @var TreeNode $treeNode */
            $treeNode = $tree;

            foreach ($pathArray as $path) {
                $treeNode = $this->addPathPartToTree($treeNode, $path, $route, $index);
            }
        }

        return $tree;
    }
724379
protected function parseTypeModifiers(array $tokens)
	{
		$modifiers = 0;
	
		foreach($tokens as $tok)
		{
			if(is_array($tok))
			{
				switch($tok[0])
				{
					case T_ABSTRACT:
						$modifiers |= MetaInfo::IS_ABSTRACT;
						break;
					case T_FINAL:
						$modifiers |= MetaInfo::IS_FINAL;
						break;
				}
			}
		}
	
		return $modifiers;
	}
780867
public function makeFields(array $fields) {
        $results = [];
        foreach($fields as $field) {
            $name = $field['name'];
            unset($field['name']);
            $results[$name] = $this->makeField($name, $field);
        }

        return $results;
    }
774764
private function replacementC($value, array $modifiers)
    {
        if (!is_array($value)) {
            return $this->implementation->reprCol($this->connection, $value);
        }
        if (isset($value[0])) {
            if ($this->prefix !== null) {
                $t = count($value) - 2;
                if (isset($value[$t])) {
                    $value[$t] = $this->prefix . $value[$t];
                }
            }
            return $this->implementation->reprChainFields($this->connection, $value);
        }
        if (isset($value['col'])) {
            $chain = array();
            foreach (array('db', 'table', 'col') as $f) {
                if (isset($value[$f])) {
                    if (is_array($value[$f])) {
                        $chain = array_merge($chain, $value[$f]);
                    } else {
                        $chain[] = $value[$f];
                    }
                }
            }
            if ($this->prefix !== null) {
                $t = count($chain) - 2;
                if (isset($chain[$t])) {
                    $chain[$t ] = $this->prefix.$chain[$t];
                }
            }
            $result = $this->implementation->reprChainFields($this->connection, $chain);
        } elseif (isset($value['value'])) {
            if (is_int($value['value'])) {
                $result = $this->implementation->reprInt($this->connection, $value['value']);
            } else {
                $result = $this->implementation->reprString($this->connection, $value['value']);
            }
            if (array_key_exists('col', $value) && isset($value['func'])) {
                $result = '';
            } else {
                $value['value'] = null;
            }
        } elseif (isset($value['func'])) {
            $result = '';
        } else {
            throw new DataInvalidFormat('col', 'required `col`, `value` or `func` field');
        }
        if (isset($value['func'])) {
            $result = $value['func'].'('.$result.')';
        }
        if (isset($value['value'])) {
            $result .= (($value['value'] > 0) ? '+' : '').(int)$value['value'];
        }
        if (isset($value['as'])) {
            $result .= ' AS '.$this->implementation->reprCol($this->connection, $value['as']);
        }
        return $result;
    }
1200801
public function transform($nodesTree, array $params = array())
    {
        $tree = array();
        foreach ($nodesTree as $node) {
            $tree[] = $this->getContext()->transform('node_tree', $node);
        }

        return $tree;
    }
973232
private function prepareFields(array $fields)
    {
        $include = null;
        foreach ($fields as $key => $type) {
            $type = (bool) $type;
            if (null === $include) {
                $include = $type;
            }
            if ($type !== $include) {
                PersisterException::badRequest('Field projection mismatch. You cannot both exclude and include fields.');
            }
            $fields[$key] = $type;
        }
        return [$fields, $include];
    }
18191
private function getMainFields(array $fields): array
    {
        $main = [];
        foreach ($fields as $field) {
            if ($field instanceof ChildrenAssociationField) {
                continue;
            }

            if ($field->is(Deferred::class)) {
                continue;
            }

            if (!$field->is(PrimaryKey::class)) {
                $main[] = $field;
                continue;
            }

            if ($field instanceof FkField) {
                $main[] = $field;
            }
        }

        return $main;
    }
314507
private List<Field> getList(Map<Class<?>, List<Field>> fields) {
        List<Field> ret = fields == null ? null : fields.get(getServiceClass());
        if (ret != null) {
            ret = Collections.unmodifiableList(ret);
        } else {
            ret = Collections.emptyList();
        }
        return ret;
    }
157498
public function buildTree(Collection $nodes, $strict = true)
    {
        $refs  = []; // Reference table to store records in the construction of the tree
        $count = 0;
        $roots = new Collection();
        foreach ($nodes as &$node) {
            /* @var Tree $node */
            $node->initChildrenRelation(); // We need to init relation to avoid LAZY LOADING in future
            $refs[$node->getKey()] = &$node; // Adding to ref table (we identify after the id)
            if ($count === 0) {
                $roots->add($node);
                $count++;
            } else {
                if ($this->siblingOfRoot($node, $roots)) { // We use this condition as a factor in building subtrees
                    $roots->add($node);
                } else { // This is not a root, so add them to the parent
                    $index = $node->{$this->getTreeColumn('parent')};
                    if (!empty($refs[$index])) { // We should already have parent for our node added to refs array
                        $refs[$index]->addChildToCollection($node);
                    } else {
                        if ($strict) { // We don't want to ignore orphan nodes
                            throw new MissingParentException();
                        }
                    }
                }
            }
        }

        if (!empty($roots)) {
            if (count($roots) > 1) {
                return $roots;
            } else {
                return $roots->first();
            }
        } else {
            return false;
        }
    }
877191
public function iterator(array $fields = null)
    {
        return $this->mapper->collection(parent::iterator($fields), $this->definition);
    }
428052
public function fields($fields)
    {
        if (!is_array($fields)) {
            $fields = func_get_args();
        }

        $this->fields = array_merge($this->fields, $fields);

        return $this;
    }
599954
protected function getAccessors(array $fields = null)
    {
        $accessors = [];

        foreach ($fields as $field) {
            if ($field->isMultipleAnswers()) {
                $content = $this->getStubContent('model-accessor-multiple-answers');

                $accessors[] = $this->getAccessor($field, $content);
            }

            if ($field->isDateOrTime()) {
                $content = $this->getStubContent('model-accessor-datetime');
                $this->replaceDateFormat($content, $field->dateFormat);

                $accessors[] = $this->getAccessor($field, $content);
            }
        }

        return implode(PHP_EOL, $accessors);
    }
372314
protected function makeMutatableFields(Collection $fields = null)
    {
        if (is_null($fields))
        {
            return '';
        }

        $mutatables = [];

        foreach ($fields as $field)
        {
            if (in_array($field->type, ['document', 'gallery', 'markdown', 'node', 'node_collection']))
            {
                $mutatables[] = "'{$field->name}' => '{$field->type}'";
            }
        }

        return implode(",", $mutatables);
    }