From ac9c3fa8248fa9bffdd0d8f5d2469807967a577c Mon Sep 17 00:00:00 2001 From: Ignace Nyamagana Butera Date: Thu, 29 Mar 2018 14:05:34 +0200 Subject: [PATCH] Rules::resolve and Rules::getPublicSuffix allow wider type parameters --- README.md | 6 ++-- bin/update-psl | 4 +-- src/Domain.php | 26 ++++++++-------- src/IDNAConverterTrait.php | 15 +++++++-- src/PublicSuffix.php | 6 ++-- src/Rules.php | 25 +++++++-------- tests/RulesTest.php | 63 +++++++++++++++++++++++++++++++++++--- 7 files changed, 107 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 04ad9de5..e081d587 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ final class Rules public static function createFromPath(string $path, $context = null): self public static function createFromString(string $content): self public function __construct(array $rules) - public function getPublicSuffix(string $domain = null, string $section = self::ALL_DOMAINS): PublicSuffix - public function resolve(string $domain = null, string $section = self::ALL_DOMAINS): Domain + public function getPublicSuffix($domain = null, string $section = self::ALL_DOMAINS): PublicSuffix + public function resolve($domain = null, string $section = self::ALL_DOMAINS): Domain } ~~~ @@ -104,6 +104,7 @@ But also enable returns informations about the domain parts and its public suffi final class Domain implements DomainInterface, JsonSerializable { + public function __construct($domain = null, PublicSuffix $publicSuffix = null) public function getPublicSuffix(): ?string public function getRegistrableDomain(): ?string public function getSubDomain(); ?string @@ -188,6 +189,7 @@ The `Rules::getPublicSuffix` method expects the same arguments as `Rules::resolv final class PublicSuffix implements DomainInterface, JsonSerializable { + public function __construct($publicSuffix = null) public function isKnown(): bool; public function isICANN(): bool; public function isPrivate(): bool; diff --git a/bin/update-psl b/bin/update-psl index 2b25cecf..284b1abe 100755 --- a/bin/update-psl +++ b/bin/update-psl @@ -1,8 +1,6 @@ #!/usr/bin/env php domain, $this->labels) = $this->setDomain($domain); $this->publicSuffix = $this->setPublicSuffix($publicSuffix ?? new PublicSuffix()); @@ -125,11 +125,10 @@ private function setRegistrableDomain() return null; } - $labels = explode('.', $this->domain); - $countLabels = count($labels); - $countPublicSuffixLabels = count($this->publicSuffix); - - return implode('.', array_slice($labels, $countLabels - $countPublicSuffixLabels - 1)); + return implode('.', array_slice( + explode('.', $this->domain), + count($this->labels) - count($this->publicSuffix) - 1 + )); } /** @@ -143,14 +142,17 @@ private function setSubDomain() return null; } - $labels = explode('.', $this->domain); - $countLabels = count($labels); - $countLabelsToRemove = count(explode('.', $this->registrableDomain)); - if ($countLabels === $countLabelsToRemove) { + $nbLabels = count($this->labels); + $nbRegistrableLabels = count($this->publicSuffix) + 1; + if ($nbLabels === $nbRegistrableLabels) { return null; } - return implode('.', array_slice($labels, 0, $countLabels - $countLabelsToRemove)); + return implode('.', array_slice( + explode('.', $this->domain), + 0, + $nbLabels - $nbRegistrableLabels + )); } /** diff --git a/src/IDNAConverterTrait.php b/src/IDNAConverterTrait.php index dd6dc23b..9e43135d 100644 --- a/src/IDNAConverterTrait.php +++ b/src/IDNAConverterTrait.php @@ -11,6 +11,8 @@ namespace Pdp; +use TypeError; + /** * @internal Domain name validator * @@ -113,14 +115,18 @@ private function idnToUnicode(string $domain): string * * For example: setDomain('wWw.uLb.Ac.be') should return ['www.ulb.ac.be', ['be', 'ac', 'ulb', 'www']]; * - * @param string|null $domain + * @param mixed $domain * * @throws Exception If the domain is invalid * * @return array */ - private function setDomain(string $domain = null): array + private function setDomain($domain = null): array { + if ($domain instanceof DomainInterface) { + return [$domain->getContent(), iterator_to_array($domain, false)]; + } + if (null === $domain) { return [$domain, []]; } @@ -129,6 +135,11 @@ private function setDomain(string $domain = null): array return [$domain, ['']]; } + if (!is_scalar($domain) && !method_exists($domain, '__toString')) { + throw new TypeError(sprintf('The domain must be a scalar, a stringable object, a DomainInterface object or null; `%s` given', gettype($domain))); + } + + $domain = (string) $domain; if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { throw new Exception(sprintf('The domain `%s` is invalid: this is an IPv4 host', $domain)); } diff --git a/src/PublicSuffix.php b/src/PublicSuffix.php index bea2e16d..e375a883 100644 --- a/src/PublicSuffix.php +++ b/src/PublicSuffix.php @@ -56,10 +56,10 @@ public static function __set_state(array $properties): self /** * New instance. * - * @param string|null $publicSuffix - * @param string $section + * @param mixed $publicSuffix + * @param string $section */ - public function __construct(string $publicSuffix = null, string $section = '') + public function __construct($publicSuffix = null, string $section = '') { list($this->publicSuffix, $this->labels) = $this->setDomain($publicSuffix); $this->section = $this->setSection($section); diff --git a/src/Rules.php b/src/Rules.php index f7ef8e8e..d34f73da 100644 --- a/src/Rules.php +++ b/src/Rules.php @@ -92,8 +92,8 @@ public function __construct(array $rules) /** * Determines the public suffix for a given domain. * - * @param string|null $domain - * @param string $section + * @param mixed $domain + * @param string $section * * @throws Exception * If the Domain is invalid or malformed @@ -102,20 +102,21 @@ public function __construct(array $rules) * * @return PublicSuffix */ - public function getPublicSuffix(string $domain = null, string $section = self::ALL_DOMAINS): PublicSuffix + public function getPublicSuffix($domain = null, string $section = self::ALL_DOMAINS): PublicSuffix { $this->validateSection($section); - if (!$this->isMatchable($domainObj = new Domain($domain))) { - throw new Exception(sprintf('The domain `%s` can not contain a public suffix', $domain)); + $domain = $domain instanceof Domain ? $domain : new Domain($domain); + if (!$this->isMatchable($domain)) { + throw new Exception(sprintf('The domain `%s` can not contain a public suffix', $domain->getContent())); } - $publicSuffix = $this->findPublicSuffix($domainObj, $section); + $publicSuffix = $this->findPublicSuffix($domain, $section); if (null === $publicSuffix->getContent()) { - $publicSuffix = new PublicSuffix($domainObj->getLabel(0)); + $publicSuffix = new PublicSuffix($domain->getLabel(0)); } static $pattern = '/[^\x20-\x7f]/'; - if (preg_match($pattern, $domainObj->getContent())) { + if (preg_match($pattern, $domain->getContent())) { return $publicSuffix->toUnicode(); } @@ -125,16 +126,16 @@ public function getPublicSuffix(string $domain = null, string $section = self::A /** * Returns PSL info for a given domain. * - * @param string|null $domain - * @param string $section + * @param mixed $domain + * @param string $section * * @return Domain */ - public function resolve(string $domain = null, string $section = self::ALL_DOMAINS): Domain + public function resolve($domain = null, string $section = self::ALL_DOMAINS): Domain { $this->validateSection($section); try { - $domain = new Domain($domain); + $domain = $domain instanceof Domain ? $domain : new Domain($domain); if (!$this->isMatchable($domain)) { return $domain; } diff --git a/tests/RulesTest.php b/tests/RulesTest.php index ac576a4a..1598c599 100644 --- a/tests/RulesTest.php +++ b/tests/RulesTest.php @@ -9,8 +9,10 @@ use Pdp\Domain; use Pdp\Exception; use Pdp\Manager; +use Pdp\PublicSuffix; use Pdp\Rules; use PHPUnit\Framework\TestCase; +use TypeError; /** * @coversDefaultClass Pdp\Rules @@ -72,6 +74,7 @@ public function testDomainInternalPhpMethod() * @covers \Pdp\PublicSuffix::setSection * @covers \Pdp\PublicSuffix::isKnown * @covers \Pdp\Domain::isKnown + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testNullWillReturnNullDomain() { @@ -79,6 +82,17 @@ public function testNullWillReturnNullDomain() $this->assertFalse($domain->isKnown()); } + + /** + * @covers ::resolve + * @covers \Pdp\IDNAConverterTrait::setDomain + */ + public function testThrowsTypeErrorOnWrongInput() + { + $this->expectException(TypeError::class); + $this->rules->resolve(date_create()); + } + /** * @covers ::resolve * @covers ::validateSection @@ -98,6 +112,7 @@ public function testResolveThrowsExceptionOnWrongDomainType() * @covers \Pdp\PublicSuffix::setSection * @covers \Pdp\PublicSuffix::isKnown * @covers \Pdp\Domain::isKnown + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testIsSuffixValidFalse() { @@ -119,6 +134,7 @@ public function testIsSuffixValidFalse() * @covers \Pdp\Domain::isKnown * @covers \Pdp\Domain::isICANN * @covers \Pdp\Domain::isPrivate + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testIsSuffixValidTrue() { @@ -142,6 +158,7 @@ public function testIsSuffixValidTrue() * @covers \Pdp\Domain::isKnown * @covers \Pdp\Domain::isICANN * @covers \Pdp\Domain::isPrivate + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testIsSuffixValidFalseWithPunycoded() { @@ -165,6 +182,7 @@ public function testIsSuffixValidFalseWithPunycoded() * @covers \Pdp\Domain::isKnown * @covers \Pdp\Domain::isICANN * @covers \Pdp\Domain::isPrivate + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testSubDomainIsNull() { @@ -177,7 +195,7 @@ public function testSubDomainIsNull() /** * @covers ::resolve * @covers ::validateSection - * @covers \Pdp\Domain::setDomain + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testWithInvalidDomainName() { @@ -191,6 +209,7 @@ public function testWithInvalidDomainName() * @covers ::findPublicSuffix * @covers ::findPublicSuffixFromSection * @covers \Pdp\PublicSuffix::setSection + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testWithPrivateDomain() { @@ -207,6 +226,7 @@ public function testWithPrivateDomain() * @covers ::findPublicSuffix * @covers ::findPublicSuffixFromSection * @covers \Pdp\PublicSuffix::setSection + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testWithPrivateDomainInvalid() { @@ -224,6 +244,7 @@ public function testWithPrivateDomainInvalid() * @covers ::findPublicSuffix * @covers ::findPublicSuffixFromSection * @covers \Pdp\PublicSuffix::setSection + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testWithPrivateDomainValid() { @@ -241,6 +262,7 @@ public function testWithPrivateDomainValid() * @covers ::findPublicSuffix * @covers ::findPublicSuffixFromSection * @covers \Pdp\PublicSuffix::setSection + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testWithICANNDomainInvalid() { @@ -252,6 +274,37 @@ public function testWithICANNDomainInvalid() $this->assertSame('ac.be', $domain->getPublicSuffix()); } + /** + * @covers ::resolve + * @covers ::validateSection + * @covers ::findPublicSuffix + * @covers ::findPublicSuffixFromSection + * @covers \Pdp\PublicSuffix::setSection + * @covers \Pdp\IDNAConverterTrait::setDomain + */ + public function testWithDomainObject() + { + $domain = new Domain('private.ulb.ac.be', new PublicSuffix('ac.be', Rules::ICANN_DOMAINS)); + $newDomain = $this->rules->resolve($domain); + $this->assertSame('private.ulb.ac.be', $domain->getDomain()); + $this->assertTrue($domain->isKnown()); + $this->assertTrue($domain->isICANN()); + $this->assertFalse($domain->isPrivate()); + $this->assertSame('ac.be', $domain->getPublicSuffix()); + $this->assertSame($domain, $newDomain); + } + + /** + * @covers ::getPublicSuffix + * @covers \Pdp\IDNAConverterTrait::setDomain + */ + public function testWithDomainInterfaceObject() + { + $publicSuffix = new PublicSuffix('ac.be', Rules::ICANN_DOMAINS); + $psl = $this->rules->getPublicSuffix($publicSuffix); + $this->assertEquals($publicSuffix, $psl); + } + /** * @dataProvider parseDataProvider * @param mixed $publicSuffix @@ -274,6 +327,7 @@ public function testGetRegistrableDomain($publicSuffix, $registrableDomain, $dom * @param mixed $domain * @param mixed $expectedDomain * @covers ::resolve + * @covers \Pdp\IDNAConverterTrait::setDomain * @covers \Pdp\Domain::setPublicSuffix * @covers \Pdp\Domain::getPublicSuffix */ @@ -289,8 +343,8 @@ public function testGetPublicSuffix($publicSuffix, $registrableDomain, $domain, * @param mixed $domain * @param mixed $expectedDomain * @covers ::resolve + * @covers \Pdp\IDNAConverterTrait::setDomain * @covers \Pdp\Domain::withPublicSuffix - * @covers \Pdp\Domain::setDomain * @covers \Pdp\Domain::getContent */ public function testGetDomain($publicSuffix, $registrableDomain, $domain, $expectedDomain) @@ -331,8 +385,8 @@ public function parseDataProvider() * * @covers ::getPublicSuffix * @covers ::validateSection - * @covers \Pdp\Domain::setDomain * @covers ::isMatchable + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testGetPublicSuffixThrowsException($domain, $section) { @@ -360,7 +414,7 @@ public function invalidParseProvider() * @covers ::getPublicSuffix * @covers ::validateSection * @covers ::isMatchable - * @covers \Pdp\PublicSuffix::setDomain + * @covers \Pdp\IDNAConverterTrait::setDomain * @dataProvider validPublicSectionProvider * * @param string|null $domain @@ -419,6 +473,7 @@ public function checkPublicSuffix($input, $expected) * @covers ::findPublicSuffixFromSection * @covers \Pdp\Domain::withPublicSuffix * @covers \Pdp\Domain::getRegistrableDomain + * @covers \Pdp\IDNAConverterTrait::setDomain */ public function testPublicSuffixSpec() {