diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fdd0d5a..97fd6c2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: operating-system: [ubuntu-latest, windows-latest, macOS-latest] - php-versions: ['7.4', '8.0'] + php-versions: ['7.4', '8.0', '8.1'] runs-on: ${{ matrix.operating-system }} steps: - name: Set autocrlf on windows @@ -20,18 +20,11 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - name: Cache composer dependencies - uses: actions/cache@v1 + - name: Install composer dependencies + uses: ramsey/composer-install@v2 with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - name: Install dependencies - run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader - - name: Check coding standanrd - run: composer cs + composer-options: "--prefer-dist --optimize-autoloader" + - name: Check coding standard + run: composer cs:check - name: Test with phpunit run: composer test diff --git a/.php_cs.dist b/.php-cs-fixer.php similarity index 100% rename from .php_cs.dist rename to .php-cs-fixer.php diff --git a/README.md b/README.md index 9016774..6ba7650 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It contains some useful functions, tests and filters missing in the original lib ## Installation -Firstly, install via Composer: +Firstly, install the package via Composer: ``` composer require susina/twig-extensions @@ -25,6 +25,15 @@ $twig = new \Twig\Environment($loader); $twig->addExtension(new \Susina\TwigExtensions\VariablesExtension()); ``` +Or, if you are working with Symfony, register the extensions you want as services and tag them as `twig.extension`: + +```yaml +// In your `services.yml` file +services: + Susina\TwigExtensions\GravatarExtension: + tags: [twig.extension] +``` + ## VariablesExtension VariablesExtension contains some tests and functions useful for manipulating variables. @@ -68,7 +77,7 @@ behaves in the same way. It can be useful if you want to generate some valid php ### Filters -`bool_to_string` filter return the string 'true' if the variable filtered can be evaluated as _true_, otherwise +`bool_to_string` filter returns the string 'true' if the variable filtered can be evaluated as _true_, otherwise it returns the string _false_: ```twig @@ -76,6 +85,14 @@ The "boolVariable" is {{ boolVariable|bool_to_string }}. ``` it returns `The "boolVariable" is true`. +You can customize the _true/false_ strings by passing two variables to the filter: the first one represents the +_true_ value, the second one the _false_ value, i.e.: + +```twig +The "boolVariable" is {{ boolVariable|bool_to_string('yes', 'no' }}. +``` +it returns `The "boolVariable" is yes`. + ## StringExtension ### Filters @@ -97,6 +114,23 @@ By default, the filter applies single quotes `'` but you can pass any character ``` then it returns `"Donald Duck"`. + +## Gravatar Extension + +Gravatar extension contain a filter to retrieve the [Gravatar](https://www.gravatar.com) image from a given email. +`gravatar` filter returns the uri for the avatar and you can easily use it in your html: + +```twig +My avatar +``` + +You can also pass some options to the filter, i.e.: +```twig +My avatar +``` + +For a full options description, please see [https://en.gravatar.com/site/implement/images/](https://en.gravatar.com/site/implement/images/). + ## Issues We manage issues and feature requests via [Github repository issues](https://github.com/susina/twig-extensions/issues). @@ -109,8 +143,8 @@ This library includes some useful composer scripts for developers: - `composer test` to run the test suite - `composer analytics` to run [Psalm](https://psalm.dev/) static analysis tool -- `composer cs-fix` to fix coding standard -- `composer cs` to check the coding standard (see https://github.com/susina/coding-standard for details) +- `composer cs:fix` to fix coding standard +- `composer cs:check` to check the coding standard (see https://github.com/susina/coding-standard for details) - `composer coverage:html` to generate code coverage report in html format (into `/coverage` directory) - `composer coverage:clover` to generate code coverage report in xml format - `composer check` runs the first three commands diff --git a/composer.json b/composer.json index f6c9973..c0ec98d 100644 --- a/composer.json +++ b/composer.json @@ -22,11 +22,12 @@ }, "require": { "php" : ">=7.4", - "twig/twig": "^3.0" + "twig/twig": "^3.0", + "symfony/options-resolver": "^5.4" }, "require-dev" : { "phpunit/phpunit": "^9.3", - "susina/coding-standard": "^1.2", + "susina/coding-standard": "^2.3", "psalm/phar": "^4.6" }, "scripts": { @@ -34,12 +35,12 @@ "check": [ "@test", "@analytics", - "@cs-fix" + "@cs:fix" ], "coverage:html": "@test --coverage-html coverage/", "coverage:clover": "@test --coverage-clover clover.xml", - "cs": "php-cs-fixer fix -v --diff --dry-run", - "cs-fix": "php-cs-fixer fix -v --diff", + "cs:check": "php-cs-fixer fix -v --diff --dry-run", + "cs:fix": "php-cs-fixer fix -v --diff", "test": "phpunit --colors=always --exclude-group=legacy" }, "scripts-descriptions": { @@ -47,8 +48,8 @@ "check": "Perform all tests and analysis, required before submitting a pull request", "coverage:html": "Create a code coverage report in html format, into the `coverage/` directory", "coverage:clover": "Create a code coverage report in xml format, into the `clover.xml` file", - "cs": "Run code style analysis, without fixing errors", - "cs-fix": "Run code style analysis and fix errors", + "cs:check": "Run code style analysis, without fixing errors", + "cs:fix": "Run code style analysis and fix errors", "test": "Run all tests" } } diff --git a/src/GravatarExtension.php b/src/GravatarExtension.php new file mode 100644 index 0000000..ff1140c --- /dev/null +++ b/src/GravatarExtension.php @@ -0,0 +1,71 @@ +configureOptions($resolver); + + $params = $resolver->resolve($params); + $hash = md5(strtolower(trim($email))); + + return self::AVATAR_URL . "/$hash" . ($params !== [] ? "?" . http_build_query($params) : ''); + } + + /** + * @psalm-suppress UnusedClosureParam $options param is mandatory even if unused. + */ + private function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefined(['size', 'forcedefault', 'default', 'rating']); + + $resolver->setAllowedTypes('size', 'int'); + $resolver->setAllowedTypes('forcedefault', 'bool'); + $resolver->setAllowedTypes('default', 'string'); + $resolver->setAllowedTypes('rating', 'string'); + + $resolver->setAllowedValues('size', fn (int $value): bool => $value >= 1 && $value <= 2048); + $resolver->setAllowedValues('default', function (string $image): bool { + return str_contains($image, '://') ? + (bool) filter_var($image, FILTER_VALIDATE_URL) : + in_array($image, ['404', 'mp', 'identicon', 'monsterid', 'wavatar', 'retro', 'robohash', 'blank']) + ; + }); + $resolver->addAllowedValues('rating', ['g', 'pg', 'r', 'x']); + + $resolver->setNormalizer('default', fn (Options $options, string $image): string => urlencode($image)); + $resolver->setNormalizer( + 'forcedefault', + fn (Options $options, bool $value): string => $value === true ? 'y' : 'n' + ); + } +} diff --git a/src/VariablesExtension.php b/src/VariablesExtension.php index 11b22c9..7971738 100644 --- a/src/VariablesExtension.php +++ b/src/VariablesExtension.php @@ -29,7 +29,7 @@ public function getTests(): array new TwigTest('object', 'is_object'), new TwigTest('scalar', 'is_scalar'), new TwigTest('string', 'is_string'), - new TwigTest('instanceOf', fn (?object $object, string $class): bool => is_a($object, $class)), + new TwigTest('instanceOf', fn (?object $object, string $class): bool => $object instanceof $class), ]; } @@ -48,7 +48,10 @@ public function getFunctions(): array public function getFilters(): array { return [ - new TwigFilter('bool_to_string', fn (bool $value): string => $value ? 'true' : 'false'), + new TwigFilter( + 'bool_to_string', + fn (bool $value, string $true = 'true', string $false = 'false'): string => $value ? $true : $false + ) ]; } } diff --git a/tests/Fixtures/filters/bool_to_string.test b/tests/Fixtures/filters/bool_to_string.test index 8508cd1..c40f272 100644 --- a/tests/Fixtures/filters/bool_to_string.test +++ b/tests/Fixtures/filters/bool_to_string.test @@ -1,10 +1,14 @@ --TEST-- "bool_to_string" filter test --TEMPLATE-- -{{ bool1|bool_to_string }} -{{ bool2|bool_to_string }} +{{ bool1 | bool_to_string }} +{{ bool2 | bool_to_string }} +{{ bool2 | bool_to_string('yes', 'no') }} +{{ bool1 | bool_to_string('light', 'darkness') }} --DATA-- return ['bool1' => true, 'bool2' => false] --EXPECT-- true -false \ No newline at end of file +false +no +light \ No newline at end of file diff --git a/tests/Fixtures/filters/gravatar.test b/tests/Fixtures/filters/gravatar.test new file mode 100644 index 0000000..dee0d61 --- /dev/null +++ b/tests/Fixtures/filters/gravatar.test @@ -0,0 +1,20 @@ +--TEST-- +"gravatar" filter test +--TEMPLATE-- +{{ email | gravatar }} +{{ email | gravatar({"size": 500}) }} +{{ email | gravatar({"forcedefault": true}) }} +{{ email | gravatar({"default": "mp"}) }} +{{ email | gravatar({"default": "https://domain.org/image.jpg"}) }} +{{ email | gravatar({"rating": "pg"}) }} +{{ email | gravatar({"size": 500, "forcedefault": true, "default": "mp", "rating": "pg"}) }} +--DATA-- +return ['email' => "email@example.org"] +--EXPECT-- +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?size=500 +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?forcedefault=y +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?default=mp +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?default=https%253A%252F%252Fdomain.org%252Fimage.jpg +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?rating=pg +https://secure.gravatar.com/avatar/8fbf4bd0581c9ccc67c560dea9931a1b?size=500&forcedefault=y&default=mp&rating=pg \ No newline at end of file diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 20b74e9..db0ac30 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -11,25 +11,27 @@ namespace Susina\TwigExtensions\Tests; +use Susina\TwigExtensions\GravatarExtension; use Susina\TwigExtensions\StringExtension; use Susina\TwigExtensions\VariablesExtension; use Twig\Test\IntegrationTestCase; class IntegrationTest extends IntegrationTestCase { - public function getExtensions() + public function getExtensions(): array { return [ new VariablesExtension(), new StringExtension(), + new GravatarExtension() ]; } /** * {@inheritdoc} */ - protected function getFixturesDir() + protected function getFixturesDir(): string { - return __DIR__.'/Fixtures'; + return __DIR__ . '/Fixtures'; } }