getMatchers() as $matcher) { $matched = $matcher::match($password, $userInputs); if (is_array($matched) && !empty($matched)) { $matches[] = $matched; } } $matches = array_merge([], ...$matches); self::usortStable($matches, [$this, 'compareMatches']); return $matches; } public function addMatcher(string $className): self { if (!is_a($className, MatchInterface::class, true)) { throw new \InvalidArgumentException(sprintf('Matcher class must implement %s', MatchInterface::class)); } $this->additionalMatchers[$className] = $className; return $this; } /** * A stable implementation of usort(). * * Whether or not the sort() function in JavaScript is stable or not is implementation-defined. * This means it's impossible for us to match all browsers exactly, but since most browsers implement sort() using * a stable sorting algorithm, we'll get the highest rate of accuracy by using a stable sort in our code as well. * * This function taken from https://github.com/vanderlee/PHP-stable-sort-functions * Copyright © 2015-2018 Martijn van der Lee (http://martijn.vanderlee.com). MIT License applies. * * @param array $array * @param callable $value_compare_func * @return bool */ public static function usortStable(array &$array, callable $value_compare_func): bool { $index = 0; foreach ($array as &$item) { $item = [$index++, $item]; } $result = usort($array, function ($a, $b) use ($value_compare_func) { $result = $value_compare_func($a[1], $b[1]); return $result == 0 ? $a[0] - $b[0] : $result; }); foreach ($array as &$item) { $item = $item[1]; } return $result; } public static function compareMatches(BaseMatch $a, BaseMatch $b): int { $beginDiff = $a->begin - $b->begin; if ($beginDiff) { return $beginDiff; } return $a->end - $b->end; } /** * Load available Match objects to match against a password. * * @return array Array of classes implementing MatchInterface */ protected function getMatchers(): array { return array_merge( self::DEFAULT_MATCHERS, array_values($this->additionalMatchers) ); } }