agent/php/ElasticApm/Impl/Util/WildcardMatcher.php (115 lines of code) (raw):

<?php /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ declare(strict_types=1); namespace Elastic\Apm\Impl\Util; /** * Code in this file is part of implementation internals and thus it is not covered by the backward compatibility. * * @internal */ final class WildcardMatcher { private const CASE_SENSITIVE_PREFIX = '(?-i)'; private const WILDCARD = '*'; /** @var int */ private static $wildcardLen; /** @var bool */ private $isCaseSensitive; /** @var bool */ private $startsWithWildcard; /** @var bool */ private $endsWithWildcard; /** @var string[] */ private $literalParts; public function __construct(string $expr) { /** @phpstan-ignore-next-line */ if (!isset(self::$wildcardLen)) { self::$wildcardLen = strlen(self::WILDCARD); } $this->isCaseSensitive = TextUtil::isPrefixOf(self::CASE_SENSITIVE_PREFIX, $expr); $exprPos = $this->isCaseSensitive ? strlen(self::CASE_SENSITIVE_PREFIX) : 0; $exprLen = strlen($expr); $this->literalParts = []; $this->startsWithWildcard = false; $lastPartWasWildcard = false; while ($exprPos < $exprLen) { $nextWildcardPos = strpos($expr, self::WILDCARD, $exprPos); if ($nextWildcardPos === $exprPos) { $lastPartWasWildcard = true; if ($this->literalParts === []) { $this->startsWithWildcard = true; } $exprPos += self::$wildcardLen; continue; } $lastPartWasWildcard = false; $literalPartEndPos = ($nextWildcardPos === false) ? $exprLen : $nextWildcardPos; $literalPartLen = $literalPartEndPos - $exprPos; $this->literalParts[] = substr($expr, /* offset */ $exprPos, /* length */ $literalPartLen); $exprPos += $literalPartLen; } $this->endsWithWildcard = $lastPartWasWildcard; } /** * @param string $haystack * @param string $needle * @param int $offset * @param bool $isCaseSensitive * * @return false|int */ private static function findSubString(string $haystack, string $needle, int $offset, bool $isCaseSensitive) { return $isCaseSensitive ? strpos($haystack, $needle, $offset) : stripos($haystack, $needle, $offset); } private static function areStringsEqual(string $str1, string $str2, bool $isCaseSensitive): bool { return $isCaseSensitive ? (strcmp($str1, $str2) === 0) : (strcasecmp($str1, $str2) === 0); } public function match(string $text): bool { if (!$this->startsWithWildcard && $this->literalParts === [] && $text !== '') { return false; } $allowAnyPrefix = $this->startsWithWildcard; $textPos = 0; $numberOfPartsToCheckInLoop = count($this->literalParts); if ($numberOfPartsToCheckInLoop > 0 && !$this->endsWithWildcard) { --$numberOfPartsToCheckInLoop; } for ($i = 0; $i < $numberOfPartsToCheckInLoop; ++$i) { $currentLiteralPart = $this->literalParts[$i]; $literalPartMatchPos = self::findSubString($text, $currentLiteralPart, $textPos, $this->isCaseSensitive); if ($literalPartMatchPos === false) { return false; } if (!$allowAnyPrefix && $literalPartMatchPos !== $textPos) { return false; } $textPos += strlen($currentLiteralPart); $allowAnyPrefix = true; } if ($numberOfPartsToCheckInLoop < count($this->literalParts)) { $lastPart = $this->literalParts[count($this->literalParts) - 1]; if (!$this->startsWithWildcard && count($this->literalParts) === 1) { if (!self::areStringsEqual($lastPart, $text, $this->isCaseSensitive)) { return false; } } else { if (!TextUtil::isSuffixOf($lastPart, $text, $this->isCaseSensitive)) { return false; } } } return true; } public function groupName(): string { $result = ''; if ($this->startsWithWildcard) { $result .= self::WILDCARD; } $isFirstLiteralPart = true; foreach ($this->literalParts as $literalPart) { if ($isFirstLiteralPart) { $isFirstLiteralPart = false; } else { $result .= self::WILDCARD; } $result .= $literalPart; } if ($this->endsWithWildcard) { $result .= self::WILDCARD; } return $result; } public function __toString(): string { $result = $this->groupName(); if ($this->isCaseSensitive) { $result = self::CASE_SENSITIVE_PREFIX . $result; } return $result; } }