diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ab3ca7ba..b2f91bc2 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -11,7 +11,7 @@ on: env: MIN_COVERED_MSI: 100 MIN_MSI: 100 - REQUIRED_PHP_EXTENSIONS: "mbstring" + REQUIRED_PHP_EXTENSIONS: "filter, mbstring" jobs: coding-standards: diff --git a/.php_cs b/.php_cs index 5aa11387..0727b5ca 100644 --- a/.php_cs +++ b/.php_cs @@ -13,16 +13,19 @@ declare(strict_types=1); use Ergebnis\PhpCsFixer\Config; -$header = <<<'EOF' -Copyright (c) 2019 Andreas Möller - +$header = Config\Header\Header::create( + Config\Header\CopyrightYears::fromYear(Config\Header\Year::fromString('2019')), + Config\Header\Author::fromString('Andreas Möller'), + Config\Header\License::fromString( + <<<'EOF' For the full copyright and license information, please view the LICENSE file that was distributed with this source code. +EOF + ), + Config\Header\Url::fromString('https://github.com/ergebnis/php-cs-fixer-config') +); -@see https://github.com/ergebnis/php-cs-fixer-config -EOF; - -$config = Config\Factory::fromRuleSet(new Config\RuleSet\Php71($header)); +$config = Config\Factory::fromRuleSet(new Config\RuleSet\Php71($header->toString())); $config->getFinder() ->ignoreDotFiles(false) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe161c82..a5fb1601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ For a full diff see [`1.1.2...master`][1.1.2...master]. For a full diff see [`1.1.1...1.1.2`][1.1.1...1.1.2]. +### Added + +* Allowed construction of header ([#23]), by [@localheinz] + ### Fixed * Brought back support for PHP 7.1 ([#17]), by [@localheinz] @@ -50,6 +54,7 @@ For a full diff see [`d899e77...1.0.0`][d899e77...1.0.0]. [#3]: https://github.com/ergebnis/php-cs-fixer-config/pull/3 [#14]: https://github.com/ergebnis/php-cs-fixer-config/pull/14 [#17]: https://github.com/ergebnis/php-cs-fixer-config/pull/17 +[#23]: https://github.com/ergebnis/php-cs-fixer-config/pull/23 [@linuxjuggler]: https://github.com/linuxjuggler [@localheinz]: https://github.com/localheinz diff --git a/composer.json b/composer.json index 45f5b9b7..581625de 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ ], "require": { "php": "^7.1", + "ext-filter": "*", "friendsofphp/php-cs-fixer": "~2.16.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 10d4486f..ab4b93e7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0e22476b9fe63a75a739d6cbd9b6bc46", + "content-hash": "c89b5bca275f3a3fab9cbd570e300a16", "packages": [ { "name": "composer/semver", @@ -4814,7 +4814,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.1" + "php": "^7.1", + "ext-filter": "*" }, "platform-dev": [] } diff --git a/src/Header/Author.php b/src/Header/Author.php new file mode 100644 index 00000000..a52802a0 --- /dev/null +++ b/src/Header/Author.php @@ -0,0 +1,50 @@ +value = $value; + } + + /** + * @param string $value + * + * @throws \InvalidArgumentException + * + * @return self + */ + public static function fromString(string $value): self + { + $trimmed = \trim($value); + + if ('' === $trimmed) { + throw new \InvalidArgumentException('Value cannot be blank or empty.'); + } + + return new self($trimmed); + } + + public function toString(): string + { + return $this->value; + } +} diff --git a/src/Header/CopyrightYears.php b/src/Header/CopyrightYears.php new file mode 100644 index 00000000..009bab86 --- /dev/null +++ b/src/Header/CopyrightYears.php @@ -0,0 +1,66 @@ +value = $value; + } + + /** + * @param Year $startYear + * @param Year $endYear + * + * @throws \InvalidArgumentException + * + * @return self + */ + public static function fromRange(Year $startYear, Year $endYear): self + { + if ($startYear->greaterThan($endYear)) { + throw new \InvalidArgumentException(\sprintf( + 'Start year "%s" needs to be equal to or less than end year "%s".', + $startYear->toString(), + $endYear->toString() + )); + } + + if ($startYear->equals($endYear)) { + return self::fromYear($startYear); + } + + return new self(\sprintf( + '%s-%s', + $startYear->toString(), + $endYear->toString() + )); + } + + public static function fromYear(Year $year): self + { + return new self($year->toString()); + } + + public function toString(): string + { + return $this->value; + } +} diff --git a/src/Header/Header.php b/src/Header/Header.php new file mode 100644 index 00000000..c7a9e2b4 --- /dev/null +++ b/src/Header/Header.php @@ -0,0 +1,65 @@ +copyrightYear = $copyrightYear; + $this->author = $author; + $this->license = $license; + $this->url = $url; + } + + public static function create(CopyrightYears $copyrightYear, Author $author, License $license, Url $url): self + { + return new self( + $copyrightYear, + $author, + $license, + $url + ); + } + + public function toString(): string + { + if ($this->license->isEmpty()) { + return <<copyrightYear->toString()} {$this->author->toString()} + +@see {$this->url->toString()} +EOF; + } + + return <<copyrightYear->toString()} {$this->author->toString()} + +{$this->license->toString()} + +@see {$this->url->toString()} +EOF; + } +} diff --git a/src/Header/License.php b/src/Header/License.php new file mode 100644 index 00000000..5363ca14 --- /dev/null +++ b/src/Header/License.php @@ -0,0 +1,42 @@ +value = $value; + } + + public static function fromString(string $value): self + { + return new self(\trim($value)); + } + + public function isEmpty(): bool + { + return '' === $this->value; + } + + public function toString(): string + { + return $this->value; + } +} diff --git a/src/Header/Url.php b/src/Header/Url.php new file mode 100644 index 00000000..ecd61eee --- /dev/null +++ b/src/Header/Url.php @@ -0,0 +1,53 @@ +value = $value; + } + + /** + * @param string $value + * + * @throws \InvalidArgumentException + * + * @return self + */ + public static function fromString(string $value): self + { + $trimmed = \trim($value); + + if (false === \filter_var($trimmed, \FILTER_VALIDATE_URL)) { + throw new \InvalidArgumentException(\sprintf( + 'Value "%s" does not appear to be a valid URL.', + $value + )); + } + + return new self($trimmed); + } + + public function toString(): string + { + return $this->value; + } +} diff --git a/src/Header/Year.php b/src/Header/Year.php new file mode 100644 index 00000000..c03c8e74 --- /dev/null +++ b/src/Header/Year.php @@ -0,0 +1,61 @@ +value = $value; + } + + /** + * @param string $value + * + * @throws \InvalidArgumentException + * + * @return self + */ + public static function fromString(string $value): self + { + if (1 !== \preg_match('/^\d{4}$/', $value)) { + throw new \InvalidArgumentException(\sprintf( + 'Value "%s" does not appear to be a valid year.', + $value + )); + } + + return new self($value); + } + + public function toString(): string + { + return $this->value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } + + public function greaterThan(self $other): bool + { + return $this->value > $other->value; + } +} diff --git a/test/Unit/Header/AuthorTest.php b/test/Unit/Header/AuthorTest.php new file mode 100644 index 00000000..8e13de27 --- /dev/null +++ b/test/Unit/Header/AuthorTest.php @@ -0,0 +1,110 @@ +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Value cannot be blank or empty.'); + + Author::fromString($value); + } + + public function provideInvalidValue(): \Generator + { + $values = [ + 'string-blank' => ' ', + 'string-empty' => '', + ]; + + foreach ($values as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideValidValue + * + * @param string $value + */ + public function testFromStringReturnsAuthor(string $value): void + { + $author = Author::fromString($value); + + self::assertSame($value, $author->toString()); + } + + public function provideValidValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideUntrimmedValue + * + * @param string $value + */ + public function testFromStringReturnsAuthorWithTrimmedValue(string $value): void + { + $author = Author::fromString($value); + + self::assertSame(\trim($value), $author->toString()); + } + + public function provideUntrimmedValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + \sprintf( + " %s \n\n", + $value + ), + ]; + } + } + + private static function validValues(): array + { + return [ + 'string-first-name' => 'Andreas', + 'string-full-name' => 'Andreas Möller', + 'string-handle' => 'localheinz', + 'string-last-name' => 'Möller', + ]; + } +} diff --git a/test/Unit/Header/CopyrightYearsTest.php b/test/Unit/Header/CopyrightYearsTest.php new file mode 100644 index 00000000..df8bd36b --- /dev/null +++ b/test/Unit/Header/CopyrightYearsTest.php @@ -0,0 +1,92 @@ +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage(\sprintf( + 'Start year "%s" needs to be equal to or less than end year "%s".', + $startYear->toString(), + $endYear->toString() + )); + + CopyrightYears::fromRange( + $startYear, + $endYear + ); + } + + public function testFromRangeReturnsCopyrightYearsWhenStartYearEqualsEndYear(): void + { + $startYear = Year::fromString('2020'); + $endYear = Year::fromString('2020'); + + $copyrightYears = CopyrightYears::fromRange( + $startYear, + $endYear + ); + + self::assertSame($startYear->toString(), $copyrightYears->toString()); + } + + public function testFromRangeReturnsCopyrightYearsWhenStartYearIsLessThanEndYear(): void + { + $startYear = Year::fromString('2019'); + $endYear = Year::fromString('2020'); + + $copyrightYears = CopyrightYears::fromRange( + $startYear, + $endYear + ); + + $expected = \sprintf( + '%s-%s', + $startYear->toString(), + $endYear->toString() + ); + + self::assertSame($expected, $copyrightYears->toString()); + } + + public function testFromYearReturnsCopyrightYears(): void + { + $year = Year::fromString('2019'); + + $copyrightYears = CopyrightYears::fromYear($year); + + self::assertSame($year->toString(), $copyrightYears->toString()); + } +} diff --git a/test/Unit/Header/HeaderTest.php b/test/Unit/Header/HeaderTest.php new file mode 100644 index 00000000..dcf6226a --- /dev/null +++ b/test/Unit/Header/HeaderTest.php @@ -0,0 +1,105 @@ +name); + + $url = Url::fromString($faker->url); + + $header = Header::create( + $copyrightYears, + $author, + License::fromString(''), + $url + ); + + $expected = <<toString()} {$author->toString()} + +@see {$url->toString()} +EOF; + + self::assertSame($expected, $header->toString()); + } + + public function testCreateReturnsHeaderWhenLicenseIsNotEmpty(): void + { + $faker = self::faker(); + + $copyrightYears = CopyrightYears::fromRange( + Year::fromString('2019'), + Year::fromString(\date('2020')) + ); + + $author = Author::fromString($faker->name); + + $license = License::fromString( + <<<'EOF' +For the full copyright and license information, please view +the LICENSE file that was distributed with this source code. +EOF + ); + + $url = Url::fromString($faker->url); + + $header = Header::create( + $copyrightYears, + $author, + $license, + $url + ); + + $expected = <<toString()} {$author->toString()} + +{$license->toString()} + +@see {$url->toString()} +EOF; + + self::assertSame($expected, $header->toString()); + } +} diff --git a/test/Unit/Header/LicenseTest.php b/test/Unit/Header/LicenseTest.php new file mode 100644 index 00000000..92bd68cc --- /dev/null +++ b/test/Unit/Header/LicenseTest.php @@ -0,0 +1,81 @@ +toString()); + } + + public function provideValidValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideUntrimmedValue + * + * @param string $value + */ + public function testFromStringReturnsLicenseWithTrimmedValue(string $value): void + { + $license = License::fromString($value); + + self::assertSame(\trim($value), $license->toString()); + } + + public function provideUntrimmedValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + \sprintf( + " %s \n\n", + $value + ), + ]; + } + } + + private static function validValues(): array + { + return [ + 'string-arbitrary' => self::faker()->realText(), + 'string-empty' => '', + ]; + } +} diff --git a/test/Unit/Header/UrlTest.php b/test/Unit/Header/UrlTest.php new file mode 100644 index 00000000..8404c294 --- /dev/null +++ b/test/Unit/Header/UrlTest.php @@ -0,0 +1,112 @@ +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage(\sprintf( + 'Value "%s" does not appear to be a valid URL.', + $value + )); + + Url::fromString($value); + } + + public function provideInvalidValue(): \Generator + { + $values = [ + 'string-arbitrary' => self::faker()->sentence, + 'string-blank' => ' ', + 'string-empty' => '', + ]; + + foreach ($values as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideValidValue + * + * @param string $value + */ + public function testFromStringReturnsUrl(string $value): void + { + $url = Url::fromString($value); + + self::assertSame($value, $url->toString()); + } + + public function provideValidValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideUntrimmedValue + * + * @param string $value + */ + public function testFromStringReturnsUrlWithTrimmedValue(string $value): void + { + $author = Url::fromString($value); + + self::assertSame(\trim($value), $author->toString()); + } + + public function provideUntrimmedValue(): \Generator + { + foreach (self::validValues() as $key => $value) { + yield $key => [ + \sprintf( + " %s \n\n", + $value + ), + ]; + } + } + + private static function validValues(): array + { + return [ + 'string-https' => 'https://github.com/ergebnis/php-cs-fixer-config', + 'string-http' => 'http://github.com/ergebnis/php-cs-fixer-config', + ]; + } +} diff --git a/test/Unit/Header/YearTest.php b/test/Unit/Header/YearTest.php new file mode 100644 index 00000000..e0abe259 --- /dev/null +++ b/test/Unit/Header/YearTest.php @@ -0,0 +1,138 @@ +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage(\sprintf( + 'Value "%s" does not appear to be a valid year.', + $value + )); + + Year::fromString($value); + } + + public function provideInvalidValue(): \Generator + { + $faker = self::faker(); + + $values = [ + 'string-arbitrary' => $faker->sentence, + 'string-blank' => ' ', + 'string-containing-year' => \sprintf( + '%s %s %s', + $faker->word, + \date('Y'), + $faker->word + ), + 'string-empty' => '', + ]; + + foreach ($values as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @dataProvider provideValidValue + * + * @param string $value + */ + public function testFromStringReturnsYear(string $value): void + { + $year = Year::fromString($value); + + self::assertSame($value, $year->toString()); + } + + public function provideValidValue(): \Generator + { + $values = [ + 'string-end' => '0000', + 'string-start' => '9999', + 'string-today' => \date('Y'), + ]; + + foreach ($values as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + public function testEqualsReturnsFalseWhenValueIsDifferent(): void + { + $one = Year::fromString('2020'); + $two = Year::fromString('2021'); + + self::assertFalse($one->equals($two)); + } + + public function testEqualsReturnsTrueWhenValueIsSame(): void + { + $value = '2020'; + + $one = Year::fromString($value); + $two = Year::fromString($value); + + self::assertTrue($one->equals($two)); + } + + public function testGreaterThanReturnsFalseWhenValueIsEqual(): void + { + $value = '2020'; + + $one = Year::fromString($value); + $two = Year::fromString($value); + + self::assertFalse($one->greaterThan($two)); + } + + public function testGreaterThanReturnsFalseWhenValueIsLess(): void + { + $one = Year::fromString('2019'); + $two = Year::fromString('2020'); + + self::assertFalse($one->greaterThan($two)); + } + + public function testGreaterThanReturnsTrueWhenValueIsGreater(): void + { + $one = Year::fromString('2020'); + $two = Year::fromString('2019'); + + self::assertTrue($one->greaterThan($two)); + } +}