<?php
 declare(strict_types=1); namespace Shopify\Auth; final class Scopes { public const SCOPE_DELIMITER = ','; private readonly array $compressedScopes; private readonly array $expandedScopes; public function __construct($scopes) { if (is_string($scopes)) { $scopesArray = explode(self::SCOPE_DELIMITER, $scopes); } else { $scopesArray = $scopes; } $scopesArray = array_unique(array_filter(array_map('trim', $scopesArray))); $impliedScopes = $this->getImpliedScopes($scopesArray); $this->compressedScopes = array_diff($scopesArray, $impliedScopes); $this->expandedScopes = array_merge($scopesArray, $impliedScopes); } public function toString(): string { return implode(self::SCOPE_DELIMITER, $this->toArray()); } public function toArray(): array { return $this->compressedScopes; } public function has($scopes): bool { if (!($scopes instanceof self)) { $scopes = new self($scopes); } return count(array_diff($scopes->toArray(), $this->expandedScopes)) === 0; } public function equals($scopes): bool { if (!($scopes instanceof self)) { $scopes = new self($scopes); } return ( count($this->compressedScopes) === count($scopes->compressedScopes) && count(array_diff($this->compressedScopes, $scopes->compressedScopes)) === 0 ); } private function getImpliedScopes(array $scopes): array { $impliedScopes = []; foreach ($scopes as $scope) { if (preg_match('/^(unauthenticated_)?write_(.*)$/', (string) $scope, $matches)) { $impliedScopes[] = ($matches[1] ?? '') . "read_{$matches[2]}"; } } return $impliedScopes; } } 