fjffk简�的咖啡店科开�2方法2
环æ»�皆山也。其西å�—诸峰,林壑尤美,望之蔚然而深秀者,ç�…ç�Šä¹Ÿã€‚山行å…七里,æ¸�闻水声潺潺,而泻出于两峰之间者,酿泉也
???????????????????????????????
??????????????/?????????????????
............/...................
.........................../...
???????????????????????????????
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>
ÿØÿà JFIF ÿÛ „ ( %!1!%)+//.383,7(-.+
-%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ ¥2" ÿÄ ÿÄ J ! 1AQ"aq�2‘#BR‚¡�Ñ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ ÿÄ * !1AQa‘"2q3�±ð#b¡ÿÚ ? ¼QxJQa�uò¸Zö Úü8,�Úú
"SSn<rçù–´âE—^ªBÖ9À\†¸Â�ÔÂ�Ã�TÂÃÛ5
ëd´³Ã�#Ã�;Þ38ϔÂ� ¶H£M:wÃŽ3…³…âpÔF&‚FK¸9„â4à GEõªfÂ�ÿ ‘ñ(ßwÂpŽF|È¥ù®häðÃ�Ѷ¹‘[Ã’inÙW¶ùñYËœQ{›K"išÒ[Ú8žë\F¹@-?v"ÔUâ€�,ìöžkÿ {I‡£šÃ�?e
rÂ�ÃV
?>
.........................................
.............................................................................
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
dsdfPK Ï$W]^<@)
)
README.mdnu „[µü¤ # Parse `robots.txt`, `robots` meta and headers
[](https://packagist.org/packages/spatie/robots-txt)
[](https://github.com/spatie/robots-txt/actions/workflows/run-tests.yml)
[](https://scrutinizer-ci.com/g/spatie/robots-txt)
[](https://packagist.org/packages/spatie/robots-txt)
Determine if a page may be crawled from robots.txt, robots meta tags and robot headers.
## Support us
[
](https://spatie.be/github-ad-click/robots-txt)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
## Installation
You can install the package via composer:
```bash
composer require spatie/robots-txt
```
## Usage
``` php
$robots = Spatie\Robots\Robots::create();
$robots->mayIndex('https://www.spatie.be/nl/admin');
$robots->mayFollowOn('https://www.spatie.be/nl/admin');
$robotsTxt = new RobotsTxt('
User-agent: *
Disallow: /admin
Crawl-delay: 1.5
');
$robotsTxt->allows('/admin', 'google'); // false
$robotsTxt->whyDisallows('/admin', 'google')[0]->userAgent; // '*'
$robotsTxt->crawlDelay('/admin', '*'); // '1.5'
```
You can also specify a user agent:
``` php
$robots = Spatie\Robots\Robots::create('UserAgent007');
```
By default, `Robots` will look for a `robots.txt` file on `https://host.com/robots.txt`.
Another location can be specified like so:
``` php
$robots = Spatie\Robots\Robots::create()
->withTxt('https://www.spatie.be/robots-custom.txt');
$robots = Spatie\Robots\Robots::create()
->withTxt(__DIR__ . '/public/robots.txt');
```
### Testing
``` bash
composer test
```
### Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
## Security Vulnerabilities
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
## Postcardware
You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.
We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).
## Credits
- [Brent Roose](https://github.com/brendt)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
PK Ï$W]øÉ&†? ? src/RobotsMeta.phpnu „[µü¤ robotsMetaTagProperties = $this->findRobotsMetaTagProperties($html);
}
public function mayIndex(): bool
{
return ! $this->noindex();
}
public function mayFollow(): bool
{
return ! $this->nofollow();
}
public function noindex(): bool
{
return $this->robotsMetaTagProperties['noindex'] ?? false;
}
public function nofollow(): bool
{
return $this->robotsMetaTagProperties['nofollow'] ?? false;
}
#[ArrayShape(['noindex' => 'bool', 'nofollow' => 'bool'])]
protected function findRobotsMetaTagProperties(string $html): array
{
$metaTagLine = $this->findRobotsMetaTagLine($html);
return [
'noindex' => $metaTagLine
? strpos(strtolower($metaTagLine), 'noindex') !== false
: false,
'nofollow' => $metaTagLine
? strpos(strtolower($metaTagLine), 'nofollow') !== false
: false,
];
}
protected function findRobotsMetaTagLine(string $html): ?string
{
if (preg_match('/\/mis', $html, $matches)) {
return $matches[0];
}
return null;
}
}
PK Ï$W]ö„„¯ç ç src/Disallow.phpnu „[µü¤ includeGlobalGroup = false;
return $this;
}
public function includeGlobalGroup(): self
{
$this->includeGlobalGroup = true;
return $this;
}
public function withPartialMatches(): self
{
$this->matchExactly = false;
return $this;
}
public function exactMatchesOnly(): self
{
$this->matchExactly = true;
return $this;
}
public static function readFrom(string $source): self
{
$content = @file_get_contents($source);
return new self($content !== false ? $content : '');
}
public function __construct(public string $content)
{
$this->disallowsPerUserAgent = $this->getDisallowsPerUserAgent($content);
$this->allowsPerUserAgent = $this->getAllowsPerUserAgent($content);
$this->crawlDelaysPerUserAgent = $this->getCrawlDelaysPerUserAgent($content);
}
public static function create(string $source): self
{
if (
strpos($source, 'http') !== false
&& strpos($source, 'robots.txt') !== false
) {
return self::readFrom($source);
}
return new self($source);
}
public function allows(string $url, ?string $userAgent = '*'): bool
{
$requestUri = '';
$parts = parse_url($url);
if ($parts !== false) {
if (isset($parts['path'])) {
$requestUri .= $parts['path'];
}
if (isset($parts['query'])) {
$requestUri .= '?'.$parts['query'];
} elseif ($this->hasEmptyQueryString($url)) {
$requestUri .= '?';
}
}
$disallowsPerUserAgent = $this->includeGlobalGroup
? $this->disallowsPerUserAgent
: array_filter($this->disallowsPerUserAgent, fn ($key) => $key !== '*', ARRAY_FILTER_USE_KEY);
$allowsPerUserAgent = $this->includeGlobalGroup
? $this->allowsPerUserAgent
: array_filter($this->allowsPerUserAgent, fn ($key) => $key !== '*', ARRAY_FILTER_USE_KEY);
$normalizedUserAgent = strtolower(trim($userAgent ?? ''));
$disallows = $this->matchExactly
? $this->getDisallowsExactly($normalizedUserAgent, $disallowsPerUserAgent)
: $this->getDisallowsContaining($normalizedUserAgent, $disallowsPerUserAgent);
$allows = $this->matchExactly
? $this->getDisallowsExactly($normalizedUserAgent, $allowsPerUserAgent)
: $this->getDisallowsContaining($normalizedUserAgent, $allowsPerUserAgent);
if ($this->pathIsExplicitAllowed($url, $allows)) {
return true;
}
$isDenied = $this->pathIsDenied($requestUri, $disallows);
if ($isDenied) {
$isAllowedWeight = $this->pathMatchWeight($requestUri, $allows);
if ($isAllowedWeight === 0) {
return false;
}
$isDisallowedWeight = $this->pathMatchWeight($requestUri, $disallows);
return $isAllowedWeight > $isDisallowedWeight;
}
return ! $isDenied;
}
/**
* @return Disallow[]|null Null if path is allowed, or a list of reasons if path is disallowed
*/
public function whyDisallows(string $path, string $userAgent): ?array
{
if ($this->allows($path, $userAgent)) {
return null;
}
$reasons = $this->getDisallows($path, $userAgent);
if ($userAgent !== '*') {
$newDisallows = $this->getDisallows($path, '*');
$reasons = array_merge($reasons, $newDisallows);
}
return $reasons;
}
/**
* @return Disallow[]
*/
protected function getDisallows(string $path, string $userAgent): array
{
$reasons = [];
foreach (array_keys($this->disallowsPerUserAgent[$userAgent] ?? []) as $disallowedPath) {
if (str_starts_with($path, $disallowedPath)) {
$reasons[] = new Disallow($userAgent, $disallowedPath);
}
}
return $reasons;
}
public function crawlDelay(string $userAgent = '*'): ?string
{
$crawlDelaysPerUserAgent = $this->includeGlobalGroup
? $this->crawlDelaysPerUserAgent
: array_filter($this->crawlDelaysPerUserAgent, fn ($key) => $key !== '*', ARRAY_FILTER_USE_KEY);
$normalizedUserAgent = strtolower(trim($userAgent ?? ''));
if (isset($crawlDelaysPerUserAgent[$normalizedUserAgent])) {
return $crawlDelaysPerUserAgent[$normalizedUserAgent];
} elseif (isset($crawlDelaysPerUserAgent['*'])) {
return $crawlDelaysPerUserAgent['*'];
}
return null;
}
protected function pathMatchWeight(string $requestUri, array $itemsPerUseragent): int
{
$weight = 0;
foreach ($itemsPerUseragent as $item => $strlen) {
$startsWith = str_starts_with($requestUri, $item);
if ($startsWith && $strlen > $weight) {
$weight = (int) $strlen;
}
}
return $weight;
}
protected function getDisallowsExactly(string $userAgent, array $disallowsPerUserAgent): array
{
return $disallowsPerUserAgent[$userAgent] ?? $disallowsPerUserAgent['*'] ?? [];
}
protected function getDisallowsContaining(string $userAgent, array $disallowsPerUserAgent): array
{
$disallows = [];
foreach ($disallowsPerUserAgent as $userAgentKey => $disallowsPerUserAgentKey) {
$contains = strpos($userAgent, $userAgentKey) !== false;
if ($contains || $userAgentKey === '*') {
$disallows = [...$disallows, ...$disallowsPerUserAgentKey];
}
}
return $disallows;
}
protected function pathIsExplicitAllowed(string $requestUri, array $allows): bool
{
return in_array($requestUri, array_keys($allows));
}
protected function pathIsDenied(string $requestUri, array $disallows): bool
{
foreach (array_keys($disallows) as $disallow) {
if ($disallow === '') {
continue;
}
$stopAtEndOfString = false;
if ($disallow[-1] === '$') {
// if the pattern ends with a dollar sign, the string must end there
$disallow = substr($disallow, 0, -1);
$stopAtEndOfString = true;
}
// convert to regexp
$disallowRegexp = preg_quote($disallow, '/');
// the pattern must start at the beginning of the string...
$disallowRegexp = '^'.$disallowRegexp;
// ...and optionally stop at the end of the string
if ($stopAtEndOfString) {
$disallowRegexp .= '$';
}
// replace (preg_quote'd) stars with an eager match
$disallowRegexp = str_replace('\\*', '.*', $disallowRegexp);
// enclose in delimiters
$disallowRegexp = '/'.$disallowRegexp.'/';
if (preg_match($disallowRegexp, $requestUri) === 1) {
return true;
}
}
return false;
}
/**
* Checks for an empty query string.
*
* This works around the fact that parse_url() will not set the 'query' key when the query string is empty.
* See: https://bugs.php.net/bug.php?id=78385
*/
protected function hasEmptyQueryString(string $url): bool
{
if ($url === '') {
return false;
}
if ($url[-1] === '?') { // ends with ?
return true;
}
if (strpos($url, '?#') !== false) { // empty query string, followed by a fragment
return true;
}
return false;
}
protected function getDisallowsPerUserAgent(string $content): array
{
$lines = explode(PHP_EOL, $content);
$lines = array_filter($lines);
$disallowsPerUserAgent = [];
$currentUserAgents = [];
$treatAllowDisallowLine = false;
foreach ($lines as $line) {
if ($this->isComment($line)) {
continue;
}
if ($this->isEmptyLine($line)) {
continue;
}
if ($this->isUserAgentLine($line)) {
if ($treatAllowDisallowLine) {
$treatAllowDisallowLine = false;
$currentUserAgents = [];
}
$disallowsPerUserAgent[$this->parseUserAgent($line)] = [];
$currentUserAgents[] = &$disallowsPerUserAgent[$this->parseUserAgent($line)];
continue;
}
if ($this->isDisallowLine($line)) {
$treatAllowDisallowLine = true;
}
if ($this->isAllowLine($line)) {
$treatAllowDisallowLine = true;
continue;
}
$disallowUrl = $this->parseDisallow($line);
foreach ($currentUserAgents as &$currentUserAgent) {
$currentUserAgent[$disallowUrl] = strlen($disallowUrl);
}
}
return $disallowsPerUserAgent;
}
protected function getAllowsPerUserAgent(string $content): array
{
$lines = explode(PHP_EOL, $content);
$lines = array_filter($lines);
$allowsPerUserAgent = [];
$currentUserAgents = [];
$treatAllowDisallowLine = false;
foreach ($lines as $line) {
if ($this->isComment($line)) {
continue;
}
if ($this->isEmptyLine($line)) {
continue;
}
if ($this->isUserAgentLine($line)) {
if ($treatAllowDisallowLine) {
$treatAllowDisallowLine = false;
$currentUserAgents = [];
}
$allowsPerUserAgent[$this->parseUserAgent($line)] = [];
$currentUserAgents[] = &$allowsPerUserAgent[$this->parseUserAgent($line)];
continue;
}
if ($this->isDisallowLine($line)) {
$treatAllowDisallowLine = true;
continue;
}
if ($this->isAllowLine($line)) {
$treatAllowDisallowLine = true;
}
$allowUrl = $this->parseAllow($line);
foreach ($currentUserAgents as &$currentUserAgent) {
$currentUserAgent[$allowUrl] = strlen($allowUrl);
}
}
$allowsPerUserAgent = array_filter($allowsPerUserAgent, function ($item) {
return count($item) > 0;
});
return $allowsPerUserAgent;
}
protected function getCrawlDelaysPerUserAgent(string $content): array
{
$lines = explode(PHP_EOL, $content);
$lines = array_filter($lines);
$crawlDelaysPerUserAgent = [];
$currentUserAgents = [];
$isUserAgentListGoing = false;
foreach ($lines as $line) {
if ($this->isComment($line)) {
continue;
}
if ($this->isEmptyLine($line)) {
continue;
}
if ($this->isUserAgentLine($line)) {
if (! $isUserAgentListGoing) {
$isUserAgentListGoing = true;
$currentUserAgents = [];
}
$userAgent = $this->parseUserAgent($line);
$currentUserAgents[] = $userAgent;
continue;
}
$isUserAgentListGoing = false;
if ($this->isCrawlDelayLine($line)) {
$crawlDelay = $this->parseCrawlDelay($line);
foreach ($currentUserAgents as $currentUserAgent) {
$crawlDelaysPerUserAgent[$currentUserAgent] = $crawlDelay;
}
}
}
return $crawlDelaysPerUserAgent;
}
protected function isComment(string $line): bool
{
return strpos(trim($line), '#') === 0;
}
protected function isEmptyLine(string $line): bool
{
return trim($line) === '';
}
protected function isUserAgentLine(string $line): bool
{
return strpos(trim(strtolower($line)), 'user-agent') === 0;
}
protected function parseUserAgent(string $line): string
{
return trim(str_replace('user-agent', '', strtolower(trim($line))), ': ');
}
protected function parseDisallow(string $line): string
{
return trim(substr_replace(trim($line), '', 0, 8), ': ');
}
protected function parseAllow(string $line): string
{
return trim(substr_replace(trim($line), '', 0, 6), ': ');
}
protected function parseCrawlDelay(string $line): string
{
return trim(substr_replace(trim($line), '', 0, 11), ': ');
}
protected function isDisallowLine(string $line): string
{
return trim(substr(str_replace(' ', '', strtolower(trim($line))), 0, 8), ': ') === 'disallow';
}
protected function isAllowLine(string $line): string
{
return trim(substr(str_replace(' ', '', strtolower(trim($line))), 0, 6), ': ') === 'allow';
}
protected function isCrawlDelayLine(string $line): string
{
return trim(substr(str_replace(' ', '', strtolower(trim($line))), 0, 11), ': ') === 'crawl-delay';
}
}
PK Ï$W]íwÔµ µ src/Robots.phpnu „[µü¤ robotsTxt = $source;
} elseif (is_string($source)) {
$this->robotsTxt = RobotsTxt::readFrom($source);
} else {
$this->robotsTxt = null;
}
}
public function withTxt(RobotsTxt|string $source): self
{
$this->robotsTxt = $source instanceof RobotsTxt
? $source
: RobotsTxt::readFrom($source);
return $this;
}
public static function create(?string $userAgent = null, ?string $source = null): self
{
return new self($userAgent, $source);
}
public function mayIndex(string $url, ?string $userAgent = null): bool
{
$userAgent = $userAgent ?? $this->userAgent;
$robotsTxt = $this->robotsTxt ?? RobotsTxt::create($this->createRobotsUrl($url));
if (! $robotsTxt->allows($url, $userAgent)) {
return false;
}
$content = $robotsTxt->content;
if ($content === false) {
throw new InvalidArgumentException("Could not read url `{$url}`");
}
return RobotsMeta::create($content)->mayIndex()
&& RobotsHeaders::create($http_response_header ?? [])->mayIndex();
}
public function mayFollowOn(string $url): bool
{
$content = @file_get_contents($url);
if ($content === false) {
throw new InvalidArgumentException("Could not read url `{$url}`");
}
return
RobotsMeta::create($content)->mayFollow()
&& RobotsHeaders::create($http_response_header ?? [])->mayFollow();
}
protected function createRobotsUrl(string $url): string
{
$robotsUrl = parse_url($url, PHP_URL_SCHEME).'://'.parse_url($url, PHP_URL_HOST);
if ($port = parse_url($url, PHP_URL_PORT)) {
$robotsUrl .= ":{$port}";
}
return "{$robotsUrl}/robots.txt";
}
}
PK Ï$W]Æ)tšê ê src/RobotsHeaders.phpnu „[µü¤ robotHeadersProperties = $this->parseHeaders($headers);
}
public function mayIndex(string $userAgent = '*'): bool
{
return $this->none($userAgent) ? false : ! $this->noindex($userAgent);
}
public function mayFollow(string $userAgent = '*'): bool
{
return $this->none($userAgent) ? false : ! $this->nofollow($userAgent);
}
public function noindex(string $userAgent = '*'): bool
{
return
$this->robotHeadersProperties[$userAgent]['noindex']
?? $this->robotHeadersProperties['*']['noindex']
?? false;
}
public function nofollow(string $userAgent = '*'): bool
{
return
$this->robotHeadersProperties[$userAgent]['nofollow']
?? $this->robotHeadersProperties['*']['nofollow']
?? false;
}
public function none(string $userAgent = '*'): bool
{
return
$this->robotHeadersProperties[$userAgent]['none']
?? $this->robotHeadersProperties['*']['none']
?? false;
}
protected function parseHeaders(array $headers): array
{
$robotHeaders = $this->filterRobotHeaders($headers);
return array_reduce($robotHeaders, function (array $parsedHeaders, $header) {
$header = $this->normalizeHeaders($header);
$headerParts = explode(':', $header);
$userAgent = count($headerParts) === 3
? trim($headerParts[1])
: '*';
$options = end($headerParts);
$parsedHeaders[$userAgent] = [
'noindex' => strpos(strtolower($options), 'noindex') !== false,
'nofollow' => strpos(strtolower($options), 'nofollow') !== false,
'none' => strpos(strtolower($options), 'none') !== false,
];
return $parsedHeaders;
}, []);
}
protected function filterRobotHeaders(array $headers): array
{
return array_filter($headers, function ($header) use ($headers) {
$headerContent = $this->normalizeHeaders($headers[$header] ?? []);
return strpos(strtolower($header), 'x-robots-tag') === 0
|| strpos(strtolower($headerContent), 'x-robots-tag') === 0;
}, ARRAY_FILTER_USE_KEY);
}
protected function normalizeHeaders($headers): string
{
return implode(',', (array) $headers);
}
}
PK Ï$W]¦A¾¯ ¯ src/WhyDisallows.phpnu „[µü¤
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK Ï$W]ÙÊ’å å .php-cs-fixer.dist.phpnu „[µü¤ in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->name('*.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
'class_attributes_separation' => [
'elements' => [
'method' => 'one',
],
],
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
],
'single_trait_insert_per_statement' => true,
])
->setFinder($finder);
PK Ï$W]^<@)
)
README.mdnu „[µü¤ PK Ï$W]øÉ&†? ? b
src/RobotsMeta.phpnu „[µü¤ PK Ï$W]ö„„¯ç ç ã src/Disallow.phpnu „[µü¤ PK Ï$W]™”°b"6 "6
src/RobotsTxt.phpnu „[µü¤ PK Ï$W]íwÔµ µ mL src/Robots.phpnu „[µü¤ PK Ï$W]Æ)tšê ê `U src/RobotsHeaders.phpnu „[µü¤ PK Ï$W]¦A¾¯ ¯ �a src/WhyDisallows.phpnu „[µü¤ PK Ï$W]ƒ· ·
‚b composer.jsonnu „[µü¤ PK Ï$W]`;Ö9B B
vf LICENSE.mdnu „[µü¤ PK Ï$W]ÙÊ’å å òj .php-cs-fixer.dist.phpnu „[µü¤ PK
p