diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..9ee0294 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,83 @@ +version: 2.1 + +executors: + poser_executor: # declares a reusable executor + docker: + - image: pugx/poser:2.0 + working_directory: ~/app + +jobs: + checkout_code: + executor: poser_executor + steps: + - checkout + - save_cache: + key: v1-repo-{{ .Environment.CIRCLE_SHA1 }} + paths: + - ~/app + + php_dependencies: + executor: poser_executor + steps: + - restore_cache: + keys: + - v1-repo-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: install project dependencies + command: composer install -n --no-progress --no-suggest + - save_cache: + paths: + - ~/app/bin + - ~/app/vendor + key: v1-php-dependencies-{{ .Environment.CIRCLE_SHA1 }} + + lint_checks: + executor: poser_executor + steps: + - restore_cache: + keys: + - v1-repo-{{ .Environment.CIRCLE_SHA1 }} + - restore_cache: + name: Restore PHP Dependencies Cache + keys: + - v1-php-dependencies-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: run php-cs-fixer checks + command: bin/php-cs-fixer fix --verbose --diff --dry-run + + phpspec_and_behat: + executor: poser_executor + steps: + - restore_cache: + keys: + - v1-repo-{{ .Environment.CIRCLE_SHA1 }} + - restore_cache: + name: Restore PHP Dependencies Cache + keys: + - v1-php-dependencies-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: run phpspec tests + command: bin/phpspec run --format=pretty + - run: + name: run phpspec coverage tests + command: bin/phpspec run -f progress -c phpspec-coverage.yml + - store_artifacts: + path: coverage + - run: + name: run behat checks + command: bin/behat + +workflows: + version: 2 + build-and-test: + jobs: + - checkout_code + - php_dependencies: + requires: + - checkout_code + - lint_checks: + requires: + - php_dependencies + - phpspec_and_behat: + requires: + - php_dependencies diff --git a/.circleci/images/Dockerfile b/.circleci/images/Dockerfile new file mode 100644 index 0000000..5bf5d59 --- /dev/null +++ b/.circleci/images/Dockerfile @@ -0,0 +1,17 @@ +FROM circleci/php:7.4-fpm-browsers + +USER root + +# install system packages +RUN apt-get install -qqy libzip-dev libpng-dev libjpeg-dev libfreetype6-dev + +# configure php packages +RUN docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ + +# install php requirements +RUN docker-php-ext-install zip iconv gd + +USER circleci + +# install prestissimo +RUN composer global require hirak/prestissimo diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7b3a741 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +custom: "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ETT4JRJARLTSC" +github: [JellyBellyDev, AlessandroMinoccheri] diff --git a/.github/ISSUE_TEMPLATE/BC_Break.md b/.github/ISSUE_TEMPLATE/BC_Break.md new file mode 100644 index 0000000..bd89906 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BC_Break.md @@ -0,0 +1,34 @@ +--- +name: πŸ’₯ BC Break +about: Have you encountered an issue during upgrade? πŸ’£ +--- + +### BC Break Report + + + +| Q | A +|------------ | ------ +| BC Break | yes +| Version | x.y.z + +#### Summary + + + +#### Previous behavior + + + +#### Current behavior + + + +#### How to reproduce + + + diff --git a/.github/ISSUE_TEMPLATE/Bug.md b/.github/ISSUE_TEMPLATE/Bug.md new file mode 100644 index 0000000..8f2b9cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug.md @@ -0,0 +1,34 @@ +--- +name: 🐞 Bug Report +about: Something is broken? πŸ”¨ +--- + +### Bug Report + + + +| Q | A +|---------------- | ------ +| BC Break | yes/no +| Library Version | x.y.z +| PHP version | x.y.z + +#### Summary + + + +#### Current behavior + + + +#### How to reproduce + + + +#### Expected behavior + + diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md new file mode 100644 index 0000000..b30d4e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_Request.md @@ -0,0 +1,19 @@ +--- +name: πŸŽ‰ Feature Request +about: You have a neat idea that should be implemented? 🎩 +--- + +### Feature Request + + + +| Q | A +|------------ | ------ +| New Feature | yes +| RFC | yes/no +| BC Break | yes/no + +#### Summary + + + diff --git a/.github/ISSUE_TEMPLATE/Support_Question.md b/.github/ISSUE_TEMPLATE/Support_Question.md new file mode 100644 index 0000000..94207fb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_Question.md @@ -0,0 +1,22 @@ +--- +name: ❓ Support Question +about: Have a problem that you can't figure out? πŸ€” +--- + + + +| Q | A +|---------------- | ------ +| Library version | x.y.z +| PHP version | x.y.z + + + + +### Support Question + + diff --git a/.github/PULL_REQUEST_TEMPLATE/Improvement.md b/.github/PULL_REQUEST_TEMPLATE/Improvement.md new file mode 100644 index 0000000..75371ba --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/Improvement.md @@ -0,0 +1,19 @@ +--- +name: βš™ Improvement +about: You have some improvement to make badge-poser better? 🎁 +--- + +### Improvement + + + +| Q | A +|------------ | ------ +| New Feature | yes +| RFC | yes/no +| BC Break | yes/no +| Issue | Close #... + +#### Summary + + diff --git a/.github/PULL_REQUEST_TEMPLATE/New_Feature.md b/.github/PULL_REQUEST_TEMPLATE/New_Feature.md new file mode 100644 index 0000000..a096114 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/New_Feature.md @@ -0,0 +1,24 @@ +--- +name: πŸŽ‰ New Feature +about: You have implemented some neat idea that you want to make part of badge-poser? 🎩 +--- + + + +### New Feature + + + +| Q | A +|------------ | ------ +| New Feature | yes +| RFC | yes/no +| BC Break | yes/no +| Issue | Close #... + +#### Summary + + + diff --git a/.gitignore b/.gitignore index 2f7a40c..9cc305e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /bin/behat /bin/php-cs-fixer /bin/phpspec +/bin/yaml-lint +/coverage /vendor +.php_cs.cache composer.lock -docker-compose.yml diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..b87a883 --- /dev/null +++ b/.php_cs @@ -0,0 +1,29 @@ +in(__DIR__) + ->exclude(['vendor']) +; + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules([ + '@Symfony' => true, + '@Symfony:risky' => true, + '@PHP71Migration:risky' => true, + '@PHPUnit60Migration:risky' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => true, + 'declare_strict_types' => false, + 'native_function_invocation' => true, + 'concat_space' => ['spacing' => 'one'], + 'binary_operator_spaces' => ['align_double_arrow' => true, 'align_equals' => true], + 'single_blank_line_at_eof' => true, + 'phpdoc_align' => ['align' => 'vertical'], + 'phpdoc_separation' => true, + 'phpdoc_summary' => false, + 'random_api_migration' => true, + ]) + ->setFinder($finder) +; diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index df21dd3..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,23 +0,0 @@ -checks: - php: - code_rating: true - duplication: true - -filter: - paths: - - src/* - -build: - dependencies: - before: - - composer require "henrikbjorn/phpspec-code-coverage" "~0.2" - environment: - php: - version: 5.6 - tests: - override: - - - command: 'bin/phpspec run -f progress -c phpspec-scrutinizer.yml' - coverage: - file: 'coverage.clover' - format: 'php-clover' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d4f3702..0000000 --- a/.travis.yml +++ /dev/null @@ -1,38 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - hhvm - -matrix: - allow_failures: - - php: 5.5 - - php: hhvm - -cache: - directories: - - $HOME/.composer/cache/files - -before_install: - - phpenv config-rm xdebug.ini || echo "xdebug not available for PHP $TRAVIS_PHP_VERSION" - -install: - - composer --prefer-dist --no-interaction update - -script: - - bin/phpspec run --format=pretty - - bin/behat - - bin/php-cs-fixer --dry-run -v fix src --rules=@PSR2 - - -notifications: - email: - - liuggio@gmail.com - - jmleroux.pro@gmail.com - -git: - depth: 10 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..24415ce --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,105 @@ +# CHANGELOG + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + + +## [Unreleased] + +### Added +* add CircleCI for build and tests +* add changelog +* configure and run php-cs-fixer with new code style roles +* add setting custom badge style from query string +* add psalm +* added all the colors from the shields.io site + +### Changed +* upgrade docker-compose for dev environment with php7.4 +* refactoring of code to work on php7.4 and drop unsupported deps +* refactoring library to full typed +* upgrade readme +* use class notation in tests +* badge style separated from file format + +### Deprecated +* this version works only with php version >= 7.4 +* rename SvgRender class to SvgPlasticRender to match the same naming pattern with other render classes + +### Removed +* travis configuration for the CI + +### Fixed +* fixup phpspec data provider +* fix dev autoloader +* remove var_dump from tests + +### Security + + +## [v1.4.1] - 2020-06-11 + +### Fixed +* fixed branch-alias in composer.json + + +## [v1.4.0] - 2020-06-04 + +### Changed +* Freeze docker-composer version to php7.1 + +### Fixed +* fixed security alert for symfony/dependency-injection package + + +## [v1.3.0] - 2019-01-03 + +### Added +* allow symfony 4 compatibility + +### Changed +* put test dependency under require-dev + + +## [v1.2.2] - 2016-03-29 + +### Changed +* extract SVG templates from renderer classes + +### Fixed +* small fixes + + +## [1.2.1] - 2016-03-16 + +### Changed +* clean code and add scrutinizer integration + + +## [v1.2.0] - 2016-03-16 + +### Added +* flat-square format + + +## [v1.1] - 2015-04-20 + +### Added +* flat version die to plastic one (added flat format) + + +## [v1.0] - 2015-01-13 +- stable release for poser + + +[unreleased]: https://github.com/badges/poser/compare/v1.4.1...HEAD +[v1.4.1]: https://github.com/badges/poser/tree/v1.4.1 +[v1.4.0]: https://github.com/badges/poser/tree/v1.4.0 +[v1.3.0]: https://github.com/badges/poser/tree/v1.3.0 +[v1.2.2]: https://github.com/badges/poser/tree/v1.2.2 +[1.2.1]: https://github.com/badges/poser/releases/tag/1.2.1 +[v1.2.0]: https://github.com/badges/poser/releases/tag/v1.2.0 +[v1.1]: https://github.com/badges/poser/releases/tag/v1.1 +[v1.0]: https://github.com/badges/poser/releases/tag/v1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f816beb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,56 @@ +# Contributing + +Active contribution and patches are very welcome. +See the [github issues](https://github.com/badges/poser/issues?state=open). +To keep things in shape we have quite a bunch of examples and features. If you're submitting pull requests please +make sure that they are still passing and if you add functionality please +take a look at the coverage as well it should be pretty high :) + +- First fork or clone the repository + +```bash +git clone git://github.com/badges/poser.git +cd poser +``` + +- Install vendors: + +```bash +composer install +``` + +- Run specs: + +```bash +composer phpspec +``` + +- Then run behat: + +```bash +composer behat +``` + + +## Using Docker + +```bash +docker-compose up --build -d +docker-compose exec fpm composer install +docker-compose exec fpm composer phpspec +docker-compose exec fpm composer behat +``` + + +## Pull Request + +Please before to push a PR execute php-cs-fixer for the code check-style and run all tests + +```bash +$ composer php-cs-fixer +$ composer phpspec +$ composer behat +``` + + +## ENJOY diff --git a/LICENSE b/LICENSE index c2f70a7..37e6808 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2013 Giulio De Donato +Copyright (c) 2004-2020 Giulio De Donato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 56117ce..66c41a9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -PHP badges poser -================ +# PHP badges poser [![CircleCI](https://circleci.com/gh/badges/poser/tree/release%2Fv2.svg?style=svg)](https://circleci.com/gh/badges/poser/tree/release%2Fv2) This is a php library that creates badges like ![Badge Poser](https://cdn.rawgit.com/badges/poser/master/badge-poser.svg) and ![I'm a badge](https://cdn.rawgit.com/badges/poser/master/i_m-badge.svg) and ![dark](https://cdn.rawgit.com/badges/poser/master/today-dark.svg), according to [Shields specification](https://github.com/badges/shields#specification). @@ -7,19 +6,21 @@ according to [Shields specification](https://github.com/badges/shields#specifica This library is used by https://poser.pugx.org [![Latest Stable Version](https://poser.pugx.org/badges/poser/version.svg)](https://packagist.org/packages/badges/poser) [![Latest Unstable Version](https://poser.pugx.org/badges/poser/v/unstable.svg)](//packagist.org/packages/badges/poser) [![Total Downloads](https://poser.pugx.org/badges/poser/downloads.svg)](https://packagist.org/packages/badges/poser) -[![Build Status](https://travis-ci.org/badges/poser.svg?branch=master)](https://travis-ci.org/badges/poser) +[![CircleCI Build](https://poser.pugx.org/badges/poser/circleci)](//packagist.org/packages/badges/poser) ## Dependencies -* PHP 5.3 or higher +* PHP 7.4 or higher * GD extension +to use the library with lower php version use the tag [v1.4](https://github.com/badges/poser/tree/v1.4.0) + ## Use as command #### 1. Create a project ``` bash -$ composer create-project badges/poser ~1.2 +$ composer create-project badges/poser $ ln -s poser/bin/poser /usr/local/bin/poser ``` @@ -35,28 +36,33 @@ Flush an image ## Usage as library -#### 1. Add to composer +#### 1. Add to composer dependencies -`composer require badges/poser ~1.2` +`$ composer require badges/poser` #### 2. Use in your project as lib -``` php - use PUGX\Poser\Render\SvgRender; - use PUGX\Poser\Poser; +```php +use PUGX\Poser\Render\SvgPlasticRender; +use PUGX\Poser\Poser; - $render = new SvgRender(); - $poser = new Poser(array($render)); +$render = new SvgPlasticRender(); +$poser = new Poser($render); - echo $poser->generate('license', 'MIT', '428F7E', 'plastic'); - // or - echo $poser->generateFromURI('license-MIT-428F7E.plastic'); - // or - $image = $poser->generate('license', 'MIT', '428F7E', 'plastic'); +echo $poser->generate('license', 'MIT', '428F7E', 'plastic'); +// or +echo $poser->generateFromURI('license-MIT-428F7E.svg?style=plastic'); +// or +echo $poser->generateFromURI('license-MIT-428F7E?style=plastic'); +// or +$image = $poser->generate('license', 'MIT', '428F7E', 'plastic'); - echo $image->getFormat(); +echo $image->getStyle(); ``` +The allowed styles are: `plastic`, `flat` and `flat-square`. + + ## Encoding Dashes `--` β†’ `-` Dash @@ -65,59 +71,23 @@ Underscores `__` β†’ `_` Underscore `_` or Space β†’ Space + ## More For *more info* please see the [behat features](./features/) and the examples in the [php-spec folder](./spec/) -## Contribution - -Active contribution and patches are very welcome. -See the [github issues](https://github.com/PUGX/poser/issues?state=open). -To keep things in shape we have quite a bunch of examples and features. If you're submitting pull requests please -make sure that they are still passing and if you add functionality please -take a look at the coverage as well it should be pretty high :) -- First fork or clone the repository +## Why a composer badge? -``` -git clone git://github.com/badges/poser.git -cd poser -``` - -- Install vendors: - -``` bash -composer install -``` +Not only because all the other languages already have it, but having the latest stable release in the readme could save time. -- Run specs: -``` bash -./bin/phpspec run --format=pretty -``` - -- Then run behat: - -``` bash -./bin/behat -``` - -## Using Docker - -We provide a `docker-compose.yml.dist` file to allow you to run tests in a Docker container. - -```bash -cp docker-compose.yml.dist docker-compose.yml -docker-compose up -d -docker-compose exec fpm composer install -docker-compose exec fpm bin/phpspec run --format=pretty -docker-compose exec fpm bin/behat -``` +## Contributing -The provided Docker compose file is for a PHP 7.1 environment, but you can modifiy it to use PHP 5.6. +Active contribution and patches are very welcome. +Please refer to [CONTRIBUTING](CONTRIBUTING.md) -See https://store.docker.com/community/images/jmleroux/fpm/tags ## License diff --git a/bin/php-parse b/bin/php-parse new file mode 120000 index 0000000..726f634 --- /dev/null +++ b/bin/php-parse @@ -0,0 +1 @@ +../vendor/nikic/php-parser/bin/php-parse \ No newline at end of file diff --git a/bin/psalm b/bin/psalm new file mode 120000 index 0000000..406781e --- /dev/null +++ b/bin/psalm @@ -0,0 +1 @@ +../vendor/vimeo/psalm/psalm \ No newline at end of file diff --git a/bin/psalm-language-server b/bin/psalm-language-server new file mode 120000 index 0000000..8b84ab4 --- /dev/null +++ b/bin/psalm-language-server @@ -0,0 +1 @@ +../vendor/vimeo/psalm/psalm-language-server \ No newline at end of file diff --git a/bin/psalm-plugin b/bin/psalm-plugin new file mode 120000 index 0000000..6f35d7f --- /dev/null +++ b/bin/psalm-plugin @@ -0,0 +1 @@ +../vendor/vimeo/psalm/psalm-plugin \ No newline at end of file diff --git a/bin/psalm-refactor b/bin/psalm-refactor new file mode 120000 index 0000000..4c2b2a3 --- /dev/null +++ b/bin/psalm-refactor @@ -0,0 +1 @@ +../vendor/vimeo/psalm/psalm-refactor \ No newline at end of file diff --git a/bin/psalter b/bin/psalter new file mode 120000 index 0000000..f30a7fb --- /dev/null +++ b/bin/psalter @@ -0,0 +1 @@ +../vendor/vimeo/psalm/psalter \ No newline at end of file diff --git a/composer.json b/composer.json index d55c889..814da26 100644 --- a/composer.json +++ b/composer.json @@ -22,24 +22,43 @@ "autoload": { "psr-4": { "PUGX\\Poser\\": "src/" } }, + "autoload-dev": { + "psr-4": { "spec\\PUGX\\": "spec/PUGX/" } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/madisoft/phpspec-data-provider-extension" + } + ], "require": { - "php": ">=5.3", + "php": ">=7.4", "ext-gd": "*", - "symfony/console": "~2.0|~3.0|~4.0" + "ext-simplexml": "*", + "symfony/console": "^4.0|^5.0" }, "require-dev": { - "phpspec/phpspec": "^2.5", - "behat/behat": "^3.4", - "friendsofphp/php-cs-fixer": "@stable", - "coduo/phpspec-data-provider-extension": "^1.0" + "phpspec/phpspec": "^6.1", + "behat/behat": "^3.7", + "friendsofphp/php-cs-fixer": "^2.16", + "moave/phpspec-data-provider-extension": "dev-master", + "friends-of-phpspec/phpspec-code-coverage": "^4.3", + "vimeo/psalm": "^3.12" }, "config": { "bin-dir": "bin" }, "bin": ["bin/poser"], + "scripts": { + "php-cs-fixer-dry-run": "bin/php-cs-fixer fix --verbose --diff --dry-run --ansi", + "php-cs-fixer": "bin/php-cs-fixer fix -v --ansi", + "phpspec": "bin/phpspec run --format=pretty --ansi", + "phpspec-coverage": "bin/phpspec run -f progress -c phpspec-coverage.yml --ansi", + "behat": "bin/behat" + }, "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.x-dev" } } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..42a29a4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.7' +services: + + fpm: + build: + context: docker/php-fpm + network: host + working_dir: /application + volumes: + - .:/application:cached diff --git a/docker-compose.yml.dist b/docker-compose.yml.dist deleted file mode 100644 index cfeb446..0000000 --- a/docker-compose.yml.dist +++ /dev/null @@ -1,17 +0,0 @@ -version: '2' - -services: - fpm: - image: jmleroux/fpm:php-7.1 - environment: - COMPOSER_HOME: /home/docker/.composer - PHP_IDE_CONFIG: 'serverName=jmleroux-cli' - PHP_XDEBUG_ENABLED: 0 - PHP_XDEBUG_IDE_KEY: XDEBUG_IDE_KEY - PHP_XDEBUG_REMOTE_HOST: xxx.xxx.xxx.xxx - XDEBUG_CONFIG: 'remote_host=xxx.xxx.xxx.xxx' - user: docker - volumes: - - ./:/srv/jmleroux - - ~/.composer:/home/docker/.composer - working_dir: /srv/jmleroux diff --git a/docker/php-fpm/Dockerfile b/docker/php-fpm/Dockerfile new file mode 100644 index 0000000..e9413e7 --- /dev/null +++ b/docker/php-fpm/Dockerfile @@ -0,0 +1,23 @@ +FROM phpdockerio/php74-fpm:latest +WORKDIR "/application" + +# Fix debconf warnings upon build +ARG DEBIAN_FRONTEND=noninteractive + +# Install selected extensions and other stuff +RUN apt-get update \ + && apt-get -y --no-install-recommends install php-xdebug php7.4-bcmath php7.4-gd php7.4-intl php-msgpack php-yaml \ + && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +# Install git +RUN apt-get update \ + && apt-get -y install git \ + && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* + +RUN mkdir -p /home/dev/.composer \ + && chown -R 1000:1000 /home/dev/ + +ENV COMPOSER_CACHE_DIR='/home/dev/.composer' + +# Install prestissimo +RUN composer global require hirak/prestissimo diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index c0299e9..0f2fc9d 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -1,14 +1,16 @@ binFolder = __DIR__ . '/../../bin/'; - $this->output = ''; + $this->output = ''; } /** * @When I run :command */ - public function iRun($command) + public function iRun($command): void { - $poser = $this->binFolder . $command; + $poser = $this->binFolder . $command; $this->return = -1; - ob_start(); - passthru("cd {$this->binFolder};php $command", $this->return); - $this->output = ob_get_clean(); + \ob_start(); + \passthru("cd {$this->binFolder};php $command", $this->return); + $this->output = \ob_get_clean(); } /** * @Then the same output should be like the content of :filePath */ - public function theSameOutputShouldBeLikeTheContentOf($filePath) + public function theSameOutputShouldBeLikeTheContentOf($filePath): void { - $filePath = __DIR__.'/../'.$filePath; - $content = file_get_contents($filePath); + $filePath = __DIR__ . '/../' . $filePath; + $content = \file_get_contents($filePath); $this->assertEquals($content, $this->output); } + /** * @Then it should pass */ - public function itShouldPass() + public function itShouldPass(): void { if (0 != $this->return) { - throw new \Exception('Error executing '.$this->return); + throw new \Exception('Error executing ' . $this->return); } } /** * @Then the content of :given should be equal to :expected */ - public function theContentOfShouldBeEqualTo($given, $expected) + public function theContentOfShouldBeEqualTo($given, $expected): void { $givenPath = $given; - $given = file_get_contents($givenPath); + $given = \file_get_contents($givenPath); - $expectedPath = __DIR__.'/../'.$expected; - $expected = file_get_contents($expectedPath); - unlink($givenPath); + $expectedPath = __DIR__ . '/../' . $expected; + $expected = \file_get_contents($expectedPath); + \unlink($givenPath); $this->assertEquals($given, $expected); } - private function assertEquals($given, $expected) + private function assertEquals($given, $expected): void { - $expected = preg_replace('/\s+/', '', $expected); - $given = preg_replace('/\s+/', '', $given); + $expected = \preg_replace('/\s+/', '', $expected); + $given = \preg_replace('/\s+/', '', $given); $perc = 0; - similar_text($expected, $given, $perc); + \similar_text($expected, $given, $perc); if ($perc < 94) { - throw new \Exception('String similarity:'.$perc.'%. String expected:'.$expected.PHP_EOL.' given:'.$given); + throw new \Exception('String similarity:' . $perc . '%. String expected:' . $expected . PHP_EOL . ' given:' . $given); } } } diff --git a/features/ui_command_creating_image_file.feature b/features/ui_command_creating_image_file.feature index e7a385e..8b70c08 100644 --- a/features/ui_command_creating_image_file.feature +++ b/features/ui_command_creating_image_file.feature @@ -5,7 +5,7 @@ Feature: Generation of an image I want to use the poser script Scenario: Create the image running a script with plastic format - When I run "poser license MIT blue -p /tmp/img.svg -f plastic" + When I run "poser license MIT blue -p /tmp/img.svg -s plastic" Then it should pass And the content of "/tmp/img.svg" should be equal to "bootstrap/fixtures/license-MIT-blue_plastic.svg" diff --git a/features/ui_command_echo_image.feature b/features/ui_command_echo_image.feature index 4a093aa..4434775 100644 --- a/features/ui_command_echo_image.feature +++ b/features/ui_command_echo_image.feature @@ -5,7 +5,7 @@ Feature: Generation of an image by echo-ing the content I want to use the poser script Scenario: Echo the image running a script with plastic format - When I run "poser license MIT blue -f plastic" + When I run "poser license MIT blue -s plastic" Then it should pass And the same output should be like the content of "bootstrap/fixtures/license-MIT-blue_plastic.svg" diff --git a/phpspec-coverage.yml b/phpspec-coverage.yml new file mode 100644 index 0000000..fcc7bf1 --- /dev/null +++ b/phpspec-coverage.yml @@ -0,0 +1,9 @@ +extensions: + Coduo\PhpSpec\DataProvider\DataProviderExtension: ~ + FriendsOfPhpSpec\PhpSpec\CodeCoverage\CodeCoverageExtension: + format: + - html + - php + output: + html: coverage/phpspec-html + php: coverage/phpspec-php/phpspec.cov diff --git a/phpspec-scrutinizer.yml b/phpspec-scrutinizer.yml deleted file mode 100644 index 2cb903a..0000000 --- a/phpspec-scrutinizer.yml +++ /dev/null @@ -1,8 +0,0 @@ -extensions: - - Coduo\PhpSpec\DataProvider\DataProviderExtension - - PhpSpec\Extension\CodeCoverageExtension -code_coverage: - format: - - clover - output: - clover: coverage.clover diff --git a/phpspec.yml b/phpspec.yml index 45b448b..7b688c4 100644 --- a/phpspec.yml +++ b/phpspec.yml @@ -1,2 +1,3 @@ extensions: - - Coduo\PhpSpec\DataProvider\DataProviderExtension + Coduo\PhpSpec\DataProvider\DataProviderExtension: ~ + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..12fd1f2 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/spec/PUGX/Poser/BadgeSpec.php b/spec/PUGX/Poser/BadgeSpec.php index df8bdde..b0c4d28 100644 --- a/spec/PUGX/Poser/BadgeSpec.php +++ b/spec/PUGX/Poser/BadgeSpec.php @@ -4,70 +4,69 @@ use PhpSpec\Exception\Exception; use PhpSpec\ObjectBehavior; -use Prophecy\Argument; use PUGX\Poser\Badge; class BadgeSpec extends ObjectBehavior { - function it_is_initializable() + public function it_is_initializable(): void { - $this->beConstructedWith('a', 'b', '97CA00', 'svg'); - $this->shouldHaveType('Pugx\Poser\Badge'); + $this->beConstructedWith('a', 'b', '97CA00', 'flat'); + $this->shouldHaveType(Badge::class); } - function it_should_be_constructed_by_fromURI_factory_method() + public function it_should_be_constructed_by_fromURI_factory_method(): void { - $this->beConstructedWith('a', 'b', '97CA00', 'svg'); + $this->beConstructedWith('a', 'b', '97CA00', 'flat'); $assert = 'version-stable-97CA00.svg'; - $it = Badge::fromURI($assert); + $it = Badge::fromURI($assert); if ((string) $it !== $assert) { - throw new Exception(sprintf("from[%s] having[%s]\n", $assert, (string) $it)); + throw new Exception(\sprintf("from[%s] having[%s]\n", $assert, (string) $it)); } } - function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_underscores() + public function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_underscores(): void { - $this->beConstructedWith('a', 'b', '97CA00', 'svg'); - $input = 'I__m__liugg__io-b-97CA00.svg'; + $this->beConstructedWith('a', 'b', '97CA00', 'flat'); + $input = 'I__m__liugg__io-b-97CA00.svg'; $assertInput = 'I_m_liugg_io-b-97CA00.svg'; - $it = Badge::fromURI($input); + $it = Badge::fromURI($input); if ((string) $it !== $assertInput) { - throw new Exception(sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); + throw new Exception(\sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); } } - function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_with_single_underscore() + public function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_with_single_underscore(): void { - $this->beConstructedWith('a', 'b', '97CA00', 'svg'); - $input = 'I_m_liuggio-b-97CA00.svg'; + $this->beConstructedWith('a', 'b', '97CA00', 'flat'); + $input = 'I_m_liuggio-b-97CA00.svg'; $assertInput = 'I m liuggio-b-97CA00.svg'; - $it = Badge::fromURI($input); + $it = Badge::fromURI($input); if ((string) $it !== $assertInput) { - throw new Exception(sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); + throw new Exception(\sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); } } - function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_with_dashes() + public function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_with_dashes(): void { - $this->beConstructedWith('a', 'b', '97CA00', 'svg'); - $input = 'I--m--liuggio-b-97CA00.svg'; + $this->beConstructedWith('a', 'b', '97CA00', 'flat'); + $input = 'I--m--liuggio-b-97CA00.svg'; $assertInput = 'I-m-liuggio-b-97CA00.svg'; - $it = Badge::fromURI($input); + $it = Badge::fromURI($input); if ((string) $it !== $assertInput) { - throw new Exception(sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); + throw new Exception(\sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); } } /** * @dataProvider positiveConversionExamples */ - function it_should_validate_available_color_schemes($colorName, $expectedValue) + public function it_should_validate_available_color_schemes($colorName, $expectedValue): void { - $this->beConstructedWith('a', 'b', $colorName, 'svg'); + $this->beConstructedWith('a', 'b', $colorName, 'flat'); $this->getHexColor()->shouldBeString(); } @@ -75,9 +74,9 @@ public function positiveConversionExamples() { $colorNames = Badge::getColorNamesAvailable(); - $data = array(); + $data = []; foreach ($colorNames as $colorName) { - $data[] = array($colorName, null); + $data[] = [$colorName, null]; } return $data; diff --git a/spec/PUGX/Poser/PoserSpec.php b/spec/PUGX/Poser/PoserSpec.php index a91bb3f..d3bf7bb 100644 --- a/spec/PUGX/Poser/PoserSpec.php +++ b/spec/PUGX/Poser/PoserSpec.php @@ -3,49 +3,94 @@ namespace spec\PUGX\Poser; use PhpSpec\ObjectBehavior; -use Prophecy\Argument; +use PUGX\Poser\Poser; use PUGX\Poser\Render\SvgFlatRender; +use PUGX\Poser\Render\SvgFlatSquareRender; class PoserSpec extends ObjectBehavior { - function let() + public function let(): void { - $render = new SvgFlatRender(); - $this->beConstructedWith(array($render)); + $this->beConstructedWith([ + new SvgFlatRender(), + new SvgFlatSquareRender(), + ]); } - function it_is_initializable() + public function it_is_initializable(): void { - $this->shouldHaveType('PUGX\Poser\Poser'); + $this->shouldHaveType(Poser::class); } - function it_should_be_able_to_generate_an_svg_image() + public function it_should_be_able_to_generate_an_svg_image(): void { $subject = 'stable'; - $status = 'v2.0'; - $color = '97CA00'; - $format = 'svg'; + $status = 'v2.0'; + $color = '97CA00'; + $style = 'flat'; + $format = 'svg'; - $this->generate($subject, $status, $color, $format)->shouldBeAValidSVGImageContaining($subject, $status); + $this->generate($subject, $status, $color, $style, $format)->shouldBeAValidSVGImageContaining($subject, $status); } - function it_should_be_able_to_generate_an_svg_image_from_URI() + public function it_should_be_able_to_generate_an_svg_image_from_URI(): void { $subject = 'stable-v2.0-97CA00.svg'; $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); } + public function it_should_be_able_to_generate_an_svg_image_from_URI_without_file_extension(): void + { + $subject = 'stable-v2.0-97CA00'; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + public function it_should_be_able_to_generate_an_svg_image_from_URI_with_style(): void + { + $subject = 'stable-v2.0-97CA00.svg?style=flat-square'; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + public function it_should_be_able_to_generate_an_svg_image_from_URI_with_empty_style(): void + { + $subject = 'stable-v2.0-97CA00.svg?style='; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + public function it_should_be_able_to_generate_an_svg_image_from_URI_with_empty_query(): void + { + $subject = 'stable-v2.0-97CA00.svg?'; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + public function it_should_be_able_to_generate_an_svg_image_from_URI_without_file_extension_with_style(): void + { + $subject = 'stable-v2.0-97CA00?style=flat-square'; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + public function it_should_throw_exception_on_generate_an_svg_image_with_bad_uri(): void + { + $subject = 'stable-v2.0-'; + + $this->shouldThrow(\InvalidArgumentException::class)->during('generateFromURI', [$subject]); + } - public function getMatchers() + public function getMatchers(): array { - return array( - 'beAValidSVGImageContaining' => function($object, $subject, $status) { - $regex = '/^$/'; - $matches = array(); + return [ + 'beAValidSVGImageContaining' => function ($object, $subject, $status) { + $regex = '/^$/'; + $matches = []; - return preg_match($regex, $object, $matches, PREG_OFFSET_CAPTURE, 0); - }, - ); + return \preg_match($regex, $object, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; } } diff --git a/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php b/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php index 734957d..d3460bf 100644 --- a/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php @@ -5,40 +5,40 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; use PUGX\Poser\Badge; +use PUGX\Poser\Calculator\TextSizeCalculatorInterface; class SvgFlatRenderSpec extends ObjectBehavior { - function let($calculator) + public function let($calculator): void { - $calculator->beADoubleOf('\PUGX\Poser\Calculator\TextSizeCalculatorInterface'); + $calculator->beADoubleOf(TextSizeCalculatorInterface::class); $calculator->calculateWidth(Argument::any())->willReturn(20); $this->beConstructedWith($calculator); } - function it_should_render_a_svg() + public function it_should_render_a_svg(): void { $badge = Badge::fromURI('version-stable-97CA00.svg'); $this->render($badge)->shouldBeAValidSVGImage(); } - public function getMatchers() + public function getMatchers(): array { - return array( - 'beAValidSVGImage' => function($subject) { - + return [ + 'beAValidSVGImage' => function ($subject) { $regex = '/^$/'; - $matches = array(); + $matches = []; - return preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); - } - ); + return \preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; } - function it_should_render_a_license_mit_exactly_like_this_svg() + public function it_should_render_a_license_mit_exactly_like_this_svg(): void { - $fixture = __DIR__ . '/../../../Fixtures/flat.svg'; - $template = file_get_contents($fixture); - $badge = Badge::fromURI('license-MIT-blue.svg'); + $fixture = __DIR__ . '/../../../Fixtures/flat.svg'; + $template = \file_get_contents($fixture); + $badge = Badge::fromURI('license-MIT-blue.svg'); $this->render($badge)->__toString()->shouldBeLike($template); } } diff --git a/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php b/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php index c476785..a141505 100644 --- a/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php @@ -5,40 +5,40 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; use PUGX\Poser\Badge; +use PUGX\Poser\Calculator\TextSizeCalculatorInterface; class SvgFlatSquareRenderSpec extends ObjectBehavior { - function let($calculator) + public function let($calculator): void { - $calculator->beADoubleOf('\PUGX\Poser\Calculator\TextSizeCalculatorInterface'); + $calculator->beADoubleOf(TextSizeCalculatorInterface::class); $calculator->calculateWidth(Argument::any())->willReturn(20); $this->beConstructedWith($calculator); } - function it_should_render_a_svg() + public function it_should_render_a_svg(): void { $badge = Badge::fromURI('version-stable-97CA00.svg'); $this->render($badge)->shouldBeAValidSVGImage(); } - public function getMatchers() + public function getMatchers(): array { - return array( + return [ 'beAValidSVGImage' => function ($subject) { - $regex = '/^$/'; - $matches = array(); + $matches = []; - return preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); - } - ); + return \preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; } - function it_should_render_a_license_mit_exactly_like_this_svg() + public function it_should_render_a_license_mit_exactly_like_this_svg(): void { - $fixture = __DIR__ . '/../../../Fixtures/flat-square.svg'; - $template = file_get_contents($fixture); - $badge = Badge::fromURI('license-MIT-blue.svg'); + $fixture = __DIR__ . '/../../../Fixtures/flat-square.svg'; + $template = \file_get_contents($fixture); + $badge = Badge::fromURI('license-MIT-blue.svg'); $this->render($badge)->__toString()->shouldBeLike($template); } } diff --git a/spec/PUGX/Poser/Render/SvgRenderSpec.php b/spec/PUGX/Poser/Render/SvgPlasticRenderSpec.php similarity index 64% rename from spec/PUGX/Poser/Render/SvgRenderSpec.php rename to spec/PUGX/Poser/Render/SvgPlasticRenderSpec.php index 50dc13c..9285b3f 100644 --- a/spec/PUGX/Poser/Render/SvgRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgPlasticRenderSpec.php @@ -5,23 +5,24 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; use PUGX\Poser\Badge; +use PUGX\Poser\Calculator\TextSizeCalculatorInterface; -class SvgRenderSpec extends ObjectBehavior +class SvgPlasticRenderSpec extends ObjectBehavior { - function let($calculator) + public function let($calculator): void { - $calculator->beADoubleOf('\PUGX\Poser\Calculator\TextSizeCalculatorInterface'); + $calculator->beADoubleOf(TextSizeCalculatorInterface::class); $calculator->calculateWidth(Argument::any())->willReturn(20); $this->beConstructedWith($calculator); } - function it_should_render_a_svg() + public function it_should_render_a_svg(): void { $badge = Badge::fromURI('version-stable-97CA00.svg'); $this->render($badge)->shouldBeAValidSVGImage(); } - function it_should_not_render_an_invalid_svg($calculator) + public function it_should_not_render_an_invalid_svg($calculator): void { $templatesDir = __DIR__ . '/../../../Fixtures/invalid_template'; $this->beConstructedWith($calculator, $templatesDir); @@ -29,7 +30,7 @@ function it_should_not_render_an_invalid_svg($calculator) $this->shouldThrow(new \RuntimeException('Generated string is not a valid XML'))->duringRender($badge); } - function it_should_not_render_non_svg_xml($calculator) + public function it_should_not_render_non_svg_xml($calculator): void { $templatesDir = __DIR__ . '/../../../Fixtures/xml_template'; $this->beConstructedWith($calculator, $templatesDir); @@ -37,16 +38,15 @@ function it_should_not_render_non_svg_xml($calculator) $this->shouldThrow(new \RuntimeException('Generated xml is not a SVG'))->duringRender($badge); } - public function getMatchers() + public function getMatchers(): array { - return array( + return [ 'beAValidSVGImage' => function ($subject) { - $regex = '/^$/'; - $matches = array(); + $matches = []; - return preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); - } - ); + return \preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; } -} +} diff --git a/src/Badge.php b/src/Badge.php index f7b0024..07524c7 100644 --- a/src/Badge.php +++ b/src/Badge.php @@ -4,108 +4,119 @@ class Badge { - const DEFAULT_FORMAT = 'svg'; - private static $colorScheme = array( - "brightgreen" => "44cc11", - "green" => "97CA00", - "yellow" => "dfb317", - "yellowgreen" => "a4a61d", - "orange" => "fe7d37", - "red" => "e05d44", - "blue" => "007ec6", - "grey" => "555555", - "lightgray" => "9f9f9f" - ); - - private $subject; - private $status; - private $color; - private $format; - - public function __construct($subject, $status, $color, $format = self::DEFAULT_FORMAT) + public const DEFAULT_STYLE = 'flat'; + public const DEFAULT_FORMAT = 'svg'; + + private static array $colorScheme = [ + 'brightgreen' => '44cc11', + 'green' => '97ca00', + 'yellowgreen' => 'a4a61d', + 'yellow' => 'dfb317', + 'orange' => 'fe7d37', + 'red' => 'e05d44', + 'blue' => '007ec6', + 'lightgray' => '9f9f9f', + 'grey' => '555555', + 'blueviolet' => '8a2be2', + 'success' => '97ca00', + 'important' => 'fe7d37', + 'critical' => 'e05d44', + 'informational' => '007ec6', + 'inactive' => '9f9f9f', + ]; + + private string $subject; + private string $status; + private string $color; + private string $style; + private string $format; + + public function __construct(string $subject, string $status, string $color, string $style = self::DEFAULT_STYLE, string $format = self::DEFAULT_FORMAT) { $this->subject = $this->escapeValue($subject); $this->status = $this->escapeValue($status); - $this->format = $this->escapeValue($format); $this->color = $this->getColorHex($color); + $this->style = $this->escapeValue($style); + $this->format = $this->escapeValue($format); if (!$this->isValidColorHex($this->color)) { - throw new \InvalidArgumentException(sprintf('Color not valid %s', $this->color)); + throw new \InvalidArgumentException(\sprintf('Color not valid %s', $this->color)); } } /** * An array of the color names available. - * - * @return array */ - public static function getColorNamesAvailable() + public static function getColorNamesAvailable(): array { - return array_keys(self::$colorScheme); + return \array_keys(self::$colorScheme); } /** * Factory method the creates a Badge from an URI - * eg. I_m-liuggio-yellow.svg + * eg. I_m-liuggio-yellow.svg. * - * @param string $URI - * - * @return Badge * @throws \InvalidArgumentException */ - public static function fromURI($URI) + public static function fromURI(string $URI): self { - $regex = '/^(([^-]|--)+)-(([^-]|--)+)-(([^-]|--)+)\.(svg|png|gif|jpg)$/'; - $match = array(); + $parsedURI = \parse_url($URI); + $path = $parsedURI['path']; + \parse_str($parsedURI['query'] ?? '', $query); - if (1 != preg_match($regex, $URI, $match) && (7 != count($match))) { - throw new \InvalidArgumentException('The URI given is not a valid URI'.$URI); - } + $regex = '/^(([^-]|--)+)-(([^-]|--)+)-(([^-.]|--)+)(\.(svg|png|gif|jpg))?$/'; + $match = []; + if (1 !== \preg_match($regex, $path, $match) && (6 > \count($match))) { + throw new \InvalidArgumentException('The URI given is not a valid URI' . $URI); + } $subject = $match[1]; $status = $match[3]; $color = $match[5]; - $format = $match[7]; + $style = isset($query['style']) && '' !== $query['style'] ? $query['style'] : self::DEFAULT_STYLE; + $format = $match[8] ?? self::DEFAULT_FORMAT; - return new self($subject, $status, $color, $format); + return new self($subject, $status, $color, $style, $format); } /** - * @return string the Hexadecimal #FFFFFF. + * @return string the Hexadecimal #FFFFFF */ - public function getHexColor() + public function getHexColor(): string { - return '#'.$this->color; + return '#' . $this->color; } /** - * @return string the format of the image eg. `svg`. + * @return string the style of the image eg. `flat`. */ - public function getFormat() + public function getStyle(): string { - return $this->format; + return $this->style; } /** - * @return string + * @return string the format of the image eg. `svg`. */ - public function getStatus() + public function getFormat(): string + { + return $this->format; + } + + public function getStatus(): string { return $this->status; } - /** - * @return string - */ - public function getSubject() + public function getSubject(): string { return $this->subject; } - public function __toString() + public function __toString(): string { - return sprintf( - "%s-%s-%s.%s", + return \sprintf( + '%s-%s-%s.%s', $this->subject, $this->status, $this->color, @@ -113,39 +124,42 @@ public function __toString() ); } - private function escapeValue($value) + private function escapeValue(string $value): string { - $pattern = array( + $pattern = [ // '/([^_])_([^_])/g', // damn it global doesn't work in PHP '/([^_])_$/', '/^_([^_])/', '/__/', '/--+/', - ); - $replacement = array( + ]; + $replacement = [ //'$1 $2', '$1 ', ' $1', '°§*ΒΌ', '-', - ); - $ret = preg_replace($pattern, $replacement, $value); - $ret = str_replace('_', ' ', $ret); // this fix the php pgrep_replace is not global :( - $ret = str_replace('°§*ΒΌ', '_', $ret); // this fix the php pgrep_replace is not global :( + ]; + $ret = \preg_replace($pattern, $replacement, $value); + $ret = \str_replace('_', ' ', $ret); // this fix the php pgrep_replace is not global :( + $ret = \str_replace('°§*ΒΌ', '_', $ret); // this fix the php pgrep_replace is not global :( return $ret; } - private function getColorHex($color) + private function getColorHex(string $color): string { - return array_key_exists($color, self::$colorScheme) ? self::$colorScheme[$color] : $color; + return \array_key_exists($color, self::$colorScheme) ? self::$colorScheme[$color] : $color; } - private function isValidColorHex($color) + /** + * @return false|int + */ + private function isValidColorHex(string $color) { - $color = ltrim($color, "#"); + $color = \ltrim($color, '#'); $regex = '/^[0-9a-fA-F]{6}$/'; - return preg_match($regex, $color); + return \preg_match($regex, $color); } } diff --git a/src/Calculator/GDTextSizeCalculator.php b/src/Calculator/GDTextSizeCalculator.php index a1be882..a1fd659 100644 --- a/src/Calculator/GDTextSizeCalculator.php +++ b/src/Calculator/GDTextSizeCalculator.php @@ -8,14 +8,15 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + namespace PUGX\Poser\Calculator; class GDTextSizeCalculator implements TextSizeCalculatorInterface { - const TEXT_FONT = '/Font/DejaVuSans.ttf'; + public const TEXT_FONT = '/Font/DejaVuSans.ttf'; /** @var string */ - protected $fontPath; + protected string $fontPath; public function __construct() { @@ -24,22 +25,17 @@ public function __construct() /** * Calculate the width of the text box. - * - * @param string $text - * @param int $size - * - * @return float */ - public function calculateWidth($text, $size = self::TEXT_SIZE) + public function calculateWidth(string $text, int $size = self::TEXT_SIZE): float { $size = $this->convertToPt($size); - $box = imagettfbbox($size, 0, $this->fontPath, $text); + $box = \imagettfbbox($size, 0, $this->fontPath, $text); - return round(abs($box[2] - $box[0]) + self::SHIELD_PADDING_EXTERNAL + self::SHIELD_PADDING_INTERNAL, 1); + return \round(\abs($box[2] - $box[0]) + self::SHIELD_PADDING_EXTERNAL + self::SHIELD_PADDING_INTERNAL, 1); } - private function convertToPt($pixels) + private function convertToPt(int $pixels): float { - return round($pixels * 0.75, 1); + return \round($pixels * 0.75, 1); } } diff --git a/src/Calculator/TextSizeCalculatorInterface.php b/src/Calculator/TextSizeCalculatorInterface.php index ae79caa..1141625 100644 --- a/src/Calculator/TextSizeCalculatorInterface.php +++ b/src/Calculator/TextSizeCalculatorInterface.php @@ -13,17 +13,12 @@ interface TextSizeCalculatorInterface { - const TEXT_SIZE = 11; - const SHIELD_PADDING_EXTERNAL = 6; - const SHIELD_PADDING_INTERNAL = 4; + public const TEXT_SIZE = 11; + public const SHIELD_PADDING_EXTERNAL = 6; + public const SHIELD_PADDING_INTERNAL = 4; /** * Calculate the width of the text box. - * - * @param string $text - * @param int $size - * - * @return float */ - public function calculateWidth($text, $size = self::TEXT_SIZE); + public function calculateWidth(string $text, int $size = self::TEXT_SIZE): float; } diff --git a/src/Image.php b/src/Image.php index 9db643c..88e58e8 100644 --- a/src/Image.php +++ b/src/Image.php @@ -12,58 +12,41 @@ namespace PUGX\Poser; /** - * Class Image, an Image value Object + * Class Image, an Image value Object. * * @author Claudio D'Alicandro * @author Giulio De Donato */ class Image { - /** - * @var string $content - */ - private $content; - /** - * @var string $format - */ - private $format; + private string $content; - /** - * @param string $content - * @param string $format - */ - private function __construct($content, $format) + private string $style; + + private function __construct(string $content, string $style) { $this->content = $content; - $this->format = $format; + $this->style = $style; } /** - * Returns the image content as binary string + * Returns the image content as binary string. */ - public function __toString() + public function __toString(): string { return (string) $this->content; } /** - * Factory method - * - * @param string $content - * @param string $format - * - * @return Image + * Factory method. */ - public static function createFromString($content, $format) + public static function createFromString(string $content, string $style): self { - return new self($content, $format); + return new self($content, $style); } - /** - * @return string - */ - public function getFormat() + public function getStyle(): string { - return $this->format; + return $this->style; } } diff --git a/src/Poser.php b/src/Poser.php index 1889f02..78c6a57 100644 --- a/src/Poser.php +++ b/src/Poser.php @@ -6,7 +6,7 @@ class Poser { - private $renders; + private array $renders; /** * Constructor. @@ -15,31 +15,30 @@ class Poser */ public function __construct($renders) { - $this->renders = array(); - if (!is_array($renders)) { - $renders = array($renders); + $this->renders = []; + if (!\is_array($renders)) { + $renders = [$renders]; } foreach ($renders as $render) { - $this->addFormatRender($render); + $this->addStyleRender($render); } } /** - * Generate and Render a badge according to the format. + * Generate and Render a badge according to the style. * * @param $subject * @param $status * @param $color + * @param $style * @param $format - * - * @return Image */ - public function generate($subject, $status, $color, $format) + public function generate(string $subject, string $status, string $color, string $style, string $format): Image { - $badge = new Badge($subject, $status, $color, $format); + $badge = new Badge($subject, $status, $color, $style, $format); - return $this->getRenderFor($badge->getFormat())->render($badge); + return $this->getRenderFor($badge->getStyle())->render($badge); } /** @@ -47,43 +46,33 @@ public function generate($subject, $status, $color, $format) * eg license-MIT-blue.svg or I_m-liuggio-yellow.svg. * * @param $string - * @return Image */ - public function generateFromURI($string) + public function generateFromURI(string $string): Image { $badge = Badge::fromURI($string); - return $this->getRenderFor($badge->getFormat())->render($badge); + return $this->getRenderFor($badge->getStyle())->render($badge); } /** - * All the formats available. - * - * @return array + * All the styles available. */ - public function validFormats() + public function validStyles(): array { - return array_keys($this->renders); + return \array_keys($this->renders); } - private function addFormatRender(RenderInterface $render) + private function addStyleRender(RenderInterface $render): void { - foreach ($render->supportedFormats() as $format) { - $this->renders[$format] = $render; - } + $this->renders[$render->getBadgeStyle()] = $render; } - /** - * @param $format - * - * @return RenderInterface - */ - private function getRenderFor($format) + private function getRenderFor(string $style): RenderInterface { - if (!isset($this->renders[$format])) { - throw new \InvalidArgumentException(sprintf('No render founds for this format [%s]', $format)); + if (!isset($this->renders[$style])) { + throw new \InvalidArgumentException(\sprintf('No render founds for this style [%s]', $style)); } - return $this->renders[$format]; + return $this->renders[$style]; } } diff --git a/src/Render/LocalSvgRenderer.php b/src/Render/LocalSvgRenderer.php index 0d25c4a..2cd45c2 100644 --- a/src/Render/LocalSvgRenderer.php +++ b/src/Render/LocalSvgRenderer.php @@ -25,23 +25,13 @@ */ abstract class LocalSvgRenderer implements RenderInterface { - const VENDOR_COLOR = '#555'; + public const VENDOR_COLOR = '#555'; - /** - * @var TextSizeCalculatorInterface - */ - private $textSizeCalculator; + private ?TextSizeCalculatorInterface $textSizeCalculator = null; - /** - * @var string - */ - private $templatesDirectory; + private ?string $templatesDirectory = null; - /** - * @param TextSizeCalculatorInterface $textSizeCalculator - * @param null|string $templatesDirectory - */ - public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null, $templatesDirectory = null) + public function __construct(?TextSizeCalculatorInterface $textSizeCalculator = null, ?string $templatesDirectory = null) { $this->textSizeCalculator = $textSizeCalculator; if (null === $this->textSizeCalculator) { @@ -54,61 +44,39 @@ public function __construct(TextSizeCalculatorInterface $textSizeCalculator = nu } } - /** - * @param Badge $badge - * - * @return mixed - */ - public function render(Badge $badge) + public function render(Badge $badge): Image { - $template = $this->getTemplate($this->getTemplateName()); + $template = $this->getTemplate($this->getTemplateName()); $parameters = $this->buildParameters($badge); - return $this->renderSvg($template, $parameters, $badge->getFormat()); + return $this->renderSvg($template, $parameters, $badge->getStyle()); } - /** - * @return string - */ - abstract protected function getTemplateName(); + abstract protected function getTemplateName(): string; /** - * @param $format - * * @return string SVG content of the template */ - private function getTemplate($format) + private function getTemplate(string $style): string { - $filepath = sprintf('%s/%s.svg', $this->templatesDirectory, $format); + $filepath = \sprintf('%s/%s.svg', $this->templatesDirectory, $style); - if (!file_exists($filepath)) { - throw new \InvalidArgumentException(sprintf('No template for format %s', $format)); + if (!\file_exists($filepath)) { + throw new \InvalidArgumentException(\sprintf('No template for style %s', $style)); } - return file_get_contents($filepath); + return \file_get_contents($filepath); } - /** - * @param $text - * - * @return float - */ - private function stringWidth($text) + private function stringWidth(string $text): float { return $this->textSizeCalculator->calculateWidth($text); } - /** - * @param string $render - * @param array $parameters - * @param string $format - * - * @return Image - */ - private function renderSvg($render, $parameters, $format) + private function renderSvg(string $render, array $parameters, string $style): Image { foreach ($parameters as $key => $variable) { - $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); + $render = \str_replace(\sprintf('{{ %s }}', $key), $variable, $render); } try { @@ -120,27 +88,22 @@ private function renderSvg($render, $parameters, $format) throw new \RuntimeException('Generated xml is not a SVG'); } - return Image::createFromString($render, $format); + return Image::createFromString($render, $style); } - /** - * @param Badge $badge - * - * @return array - */ - private function buildParameters(Badge $badge) + private function buildParameters(Badge $badge): array { - $parameters = array(); - - $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); - $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); - $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; - $parameters['vendorColor'] = static::VENDOR_COLOR; - $parameters['valueColor'] = $badge->getHexColor(); - $parameters['vendor'] = $badge->getSubject(); - $parameters['value'] = $badge->getStatus(); - $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; - $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; + $parameters = []; + + $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); + $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); + $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; + $parameters['vendorColor'] = static::VENDOR_COLOR; + $parameters['valueColor'] = $badge->getHexColor(); + $parameters['vendor'] = $badge->getSubject(); + $parameters['value'] = $badge->getStatus(); + $parameters['vendorStartPosition'] = \round($parameters['vendorWidth'] / 2, 1) + 1; + $parameters['valueStartPosition'] = $parameters['vendorWidth'] + \round($parameters['valueWidth'] / 2, 1) - 1; return $parameters; } diff --git a/src/Render/RenderInterface.php b/src/Render/RenderInterface.php index b27d78e..3f5b422 100644 --- a/src/Render/RenderInterface.php +++ b/src/Render/RenderInterface.php @@ -12,20 +12,17 @@ namespace PUGX\Poser\Render; use PUGX\Poser\Badge; +use PUGX\Poser\Image; interface RenderInterface { /** - * Render a badge. - * - * @param Badge $badge - * - * @return \PUGX\Poser\Image + * Render a badge */ - public function render(Badge $badge); + public function render(Badge $badge): Image; /** - * @return array the list of the supported format eg array('svg') + * @return string the style of the badge image eg. `flat`. */ - public function supportedFormats(); + public function getBadgeStyle(): string; } diff --git a/src/Render/SvgFlatRender.php b/src/Render/SvgFlatRender.php index 9ef9e93..b387566 100644 --- a/src/Render/SvgFlatRender.php +++ b/src/Render/SvgFlatRender.php @@ -12,24 +12,19 @@ namespace PUGX\Poser\Render; /** - * Class SvgFlatGenerator + * Class SvgFlatRender. * * @author Giulio De Donato */ class SvgFlatRender extends LocalSvgRenderer { - /** - * A list of all supported formats. - * - * @return array - */ - public function supportedFormats() + public function getBadgeStyle(): string { - return array('flat', 'svg'); + return 'flat'; } - protected function getTemplateName() + protected function getTemplateName(): string { - return 'flat'; + return $this->getBadgeStyle(); } } diff --git a/src/Render/SvgFlatSquareRender.php b/src/Render/SvgFlatSquareRender.php index 439fbb2..979bb41 100644 --- a/src/Render/SvgFlatSquareRender.php +++ b/src/Render/SvgFlatSquareRender.php @@ -12,24 +12,19 @@ namespace PUGX\Poser\Render; /** - * Class SvgFlatGenerator + * Class SvgFlatSquareRender. * * @author Giulio De Donato */ class SvgFlatSquareRender extends LocalSvgRenderer { - /** - * A list of all supported formats. - * - * @return array - */ - public function supportedFormats() + public function getBadgeStyle(): string { - return array('flat-square'); + return 'flat-square'; } - protected function getTemplateName() + protected function getTemplateName(): string { - return 'flat-square'; + return $this->getBadgeStyle(); } } diff --git a/src/Render/SvgRender.php b/src/Render/SvgPlasticRender.php similarity index 60% rename from src/Render/SvgRender.php rename to src/Render/SvgPlasticRender.php index 64812ea..69b7112 100644 --- a/src/Render/SvgRender.php +++ b/src/Render/SvgPlasticRender.php @@ -11,28 +11,21 @@ namespace PUGX\Poser\Render; -use PUGX\Poser\Badge; - /** - * Class SvgGenerator + * Class SvgPlasticRender. * * @author Claudio D'Alicandro * @author Giulio De Donato */ -class SvgRender extends LocalSvgRenderer +class SvgPlasticRender extends LocalSvgRenderer { - /** - * A list of all supported formats. - * - * @return array - */ - public function supportedFormats() + public function getBadgeStyle(): string { - return array('plastic'); + return 'plastic'; } - protected function getTemplateName() + protected function getTemplateName(): string { - return 'plastic'; + return $this->getBadgeStyle(); } } diff --git a/src/UI/Command.php b/src/UI/Command.php index 4cd308b..0545285 100644 --- a/src/UI/Command.php +++ b/src/UI/Command.php @@ -2,20 +2,20 @@ namespace PUGX\Poser\UI; +use PUGX\Poser\Badge; +use PUGX\Poser\Poser; use PUGX\Poser\Render\SvgFlatRender; use PUGX\Poser\Render\SvgFlatSquareRender; +use PUGX\Poser\Render\SvgPlasticRender; use Symfony\Component\Console\Command\Command as BaseCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use PUGX\Poser\Badge; -use PUGX\Poser\Poser; -use PUGX\Poser\Render\SvgRender; class Command extends BaseCommand { - const HEADER = " ________________ + public const HEADER = " ________________ |_ _ _| _ _ _ _ _ _ _ | |_)(_|(_|(_|(/_ |_)(_)_\(/_| | _| _|______________| @@ -23,27 +23,21 @@ class Command extends BaseCommand http://poser.pug.org "; - /** @var Poser */ - private $poser; - - /** @var string */ - protected $format; + private Poser $poser; - /** @var string */ - protected $header; + protected string $header; - private function init() + private function init(): void { - $this->poser = new Poser(array( - new SvgRender(), + $this->poser = new Poser([ + new SvgPlasticRender(), new SvgFlatRender(), - new SvgFlatSquareRender() - )); - $this->format = 'flat'; + new SvgFlatSquareRender(), + ]); $this->header = self::HEADER; } - protected function configure() + protected function configure(): void { $this->init(); @@ -63,13 +57,21 @@ protected function configure() ->addArgument( 'color', InputArgument::OPTIONAL, - 'The hexadecimal color eg. `97CA00` or the name ['.join(', ', Badge::getColorNamesAvailable()).']' + 'The hexadecimal color eg. `97CA00` or the name [' . \implode(', ', Badge::getColorNamesAvailable()) . ']' + ) + ->addOption( + 'style', + 's', + InputOption::VALUE_REQUIRED, + 'The style of the image eg. `flat`, styles available [' . \implode(', ', $this->poser->validStyles()) . ']', + Badge::DEFAULT_STYLE ) ->addOption( 'format', 'f', InputOption::VALUE_REQUIRED, - 'The format of the image eg. `svg`, formats available ['.join(', ', $this->poser->validFormats()).']' + 'The format of the image eg. `svg`, formats available [' . \implode(', ', $this->poser->validStyles()) . ']', + Badge::DEFAULT_FORMAT ) ->addOption( 'path', @@ -79,18 +81,19 @@ protected function configure() ); } - protected function execute(InputInterface $input, OutputInterface $output) + /** + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { $subject = $input->getArgument('subject'); - $status = $input->getArgument('status'); - $color = $input->getArgument('color'); - - if ($input->getOption('format')) { - $this->format = $input->getOption('format'); - } + $status = $input->getArgument('status'); + $color = $input->getArgument('color'); + $style = (string) $input->getOption('style'); + $format = (string) $input->getOption('format'); try { - $imageContent = $this->poser->generate($subject, $status, $color, $this->format); + $imageContent = $this->poser->generate($subject, $status, $color, $style, $format); if ($input->getOption('path')) { $this->storeImage($output, $input->getOption('path'), $imageContent); @@ -101,19 +104,24 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->printHeaderOnce($output); throw $e; } + + return 0; } - protected function flushImage(OutputInterface $output, $imageContent) + protected function flushImage(OutputInterface $output, string $imageContent): void { - $output->write((string) $imageContent); + $output->write($imageContent); $this->header = ''; } - protected function storeImage(OutputInterface $output, $path, $imageContent) + /** + * @throws \Exception + */ + protected function storeImage(OutputInterface $output, string $path, string $imageContent): void { $this->printHeaderOnce($output); try { - $fp = @fopen($path, "x"); + $fp = @\fopen($path, 'x'); } catch (\Exception $e) { $fp = false; } @@ -121,16 +129,16 @@ protected function storeImage(OutputInterface $output, $path, $imageContent) if (false == $fp) { throw new \Exception("Error on creating the file maybe file [$path] already exists?"); } - $written = @fwrite($fp, $imageContent); - if ($written <1 || $written != strlen($imageContent)) { + $written = @\fwrite($fp, $imageContent); + if ($written < 1 || $written != \strlen($imageContent)) { throw new \Exception('Error on writing to file.'); } - @fclose($fp); + @\fclose($fp); - $output->write(sprintf('Image created at %s', $path)); + $output->write(\sprintf('Image created at %s', $path)); } - protected function printHeaderOnce(OutputInterface $output) + protected function printHeaderOnce(OutputInterface $output): void { if (!empty($this->header)) { $output->write($this->header); diff --git a/src/UI/SingleCommandApplication.php b/src/UI/SingleCommandApplication.php index 545977f..2fca0aa 100644 --- a/src/UI/SingleCommandApplication.php +++ b/src/UI/SingleCommandApplication.php @@ -2,9 +2,9 @@ namespace PUGX\Poser\UI; +use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Application; /** * Application providing access to just one command. @@ -26,10 +26,9 @@ class SingleCommandApplication extends Application { /** - * Name of the single accessible command of this application - * @var string + * Name of the single accessible command of this application. */ - private $commandName; + private ?string $commandName = null; /** * Constructor to build a "single command" application, given a command. @@ -39,13 +38,13 @@ class SingleCommandApplication extends Application * @param Command $command The single (accessible) command for this application * @param string $version The version of the application */ - public function __construct(Command $command, $version = 'UNKNOWN') + public function __construct(Command $command, string $version = 'UNKNOWN') { parent::__construct($command->getName(), $version); // Add the given command as single (accessible) command. $this->add($command); - $this->commandName = $command->getName(); + $this->commandName = (string) $command->getName(); // Override the Application's definition so that it does not // require a command name as first argument. @@ -55,7 +54,7 @@ public function __construct(Command $command, $version = 'UNKNOWN') /** * {@inheritdoc} */ - protected function getCommandName(InputInterface $input) + protected function getCommandName(InputInterface $input): ?string { return $this->commandName; } @@ -75,11 +74,11 @@ protected function getCommandName(InputInterface $input) * * @throws \LogicException */ - public function add(Command $command) + public function add(Command $command): ?Command { // Fail if we already set up the single accessible command. if ($this->commandName) { - throw new \LogicException("You should not add additional commands to a SingleCommandApplication instance."); + throw new \LogicException('You should not add additional commands to a SingleCommandApplication instance.'); } return parent::add($command);