diff --git a/.gitignore b/.gitignore index d484c87..3722a23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/bin -/vendor +/bin/behat +/bin/phpspec +/vendor \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 21b5433..29321dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,6 @@ php: - 5.5 - hhvm -services: - - redis-server - matrix: allow_failures: - php: hhvm @@ -17,7 +14,8 @@ before_script: - COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev --no-interaction install script: - - bin/phpunit -v + - bin/phpspec run --format=pretty + - bin/behat notifications: email: diff --git a/README.md b/README.md index 9592d55..d1b35e9 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,106 @@ PHP badges poser ================ This is a php library that creates badges, -according to [Shields specification](https://github.com/badges/shields#specification). \ No newline at end of file +according to [Shields specification](https://github.com/badges/shields#specification). + +``` bash + ________________ + |_ _ _| _ _ | _ _ _ _ _ | + |_)(_|(_|(_|(/_ | |_)(_)_\(/_| | + _| |_|______________| + +``` + +This library create this: ![badge-poser](badge-poser.svg) and ![I'm a badge](i_m-badge.svg) and ![dark](today-dark.svg)... + +This library is used by the most used badge creator in php https://poser.pugx.org/ + +## Usage as library + +#### 1. Add to composer + +`composer require badge/poser ~0.1` + +#### 2. Use in your project as lib + +``` php + use PUGX\Poser\Render\SvgRender; + use PUGX\Poser\Poser; + + $render = new SvgRender(); + $poser = new Poser(array('svg' => $render)); + + echo $poser->generate('license', 'MIT', '428F7E', 'svg'); + // or + echo $poser->generateFromURI('license-MIT-428F7E.svg'); +``` + +## Use in your project as command + +#### 1. Add to composer + +`$ composer require badges/poser ~0.1` + +or create a project + +`$ composer create-project badges/poser ~0.1` + +and make the link + +`$ ln -s poser/bin/poser /usr/local/bin/poser` + +#### 2. lunch the command + +Creating an image + +`$ poser license MIT blue -p "license.svg"` + +Flushing the image + +`$ poser license MIT blue` + +## Encoding + +Dashes `--` → `-` Dash + +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 + +``` +git clone git://github.com/badges/poser.git +cd poser +``` + +- Install vendors: + +``` bash +composer install +``` + +- Run specs: + +``` bash +./bin/phpspec run --format=pretty +``` + +- Then run behat: + +``` bash +./bin/behat run +``` diff --git a/badge-poser.svg b/badge-poser.svg new file mode 100644 index 0000000..c2b23f6 --- /dev/null +++ b/badge-poser.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + badge + badge + poser + poser + + \ No newline at end of file diff --git a/bin/poser b/bin/poser new file mode 100755 index 0000000..421c83a --- /dev/null +++ b/bin/poser @@ -0,0 +1,24 @@ +#!/usr/bin/env php +run(); \ No newline at end of file diff --git a/composer.json b/composer.json index 7f431ec..8f72a94 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "pugx/poser", - "description": "Poser, add badges on your readme, such as downloads number or latest version.", + "name": "badges/poser", + "description": "Poser, add badges on your readme..", "keywords": ["composer", "packagist", "badge", "pin"], "homepage": "https://github.com/PUGX", "type": "lib", @@ -20,17 +20,20 @@ } ], "autoload": { - "psr-4": { "PUGX\\Badge\\Poser": "src/" } + "psr-4": { "PUGX\\Poser\\": "src/" } }, "require": { + "symfony/console": "dev-master" }, "require-dev": { - "phpunit/phpunit": "@stable" + "phpspec/phpspec": "dev-master", + "behat/behat": "dev-master" }, "config": { "bin-dir": "bin" }, - "minimum-stability": "stable", + "bin": ["bin/poser"], + "minimum-stability": "dev", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..a68c619 --- /dev/null +++ b/composer.lock @@ -0,0 +1,903 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "f8400eae9ffc9327de6718202f9e4c2f", + "packages": [ + { + "name": "symfony/console", + "version": "dev-master", + "target-dir": "Symfony/Component/Console", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "9a491e7983209bf320d40917291c03e856eee947" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/9a491e7983209bf320d40917291c03e856eee947", + "reference": "9a491e7983209bf320d40917291c03e856eee947", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1", + "symfony/process": "~2.1" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "http://symfony.com", + "time": "2014-06-20 17:41:51" + } + ], + "packages-dev": [ + { + "name": "behat/behat", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Behat/Behat.git", + "reference": "c0409b8a44dd363ea69a39428d131b1ae3581814" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Behat/zipball/c0409b8a44dd363ea69a39428d131b1ae3581814", + "reference": "c0409b8a44dd363ea69a39428d131b1ae3581814", + "shasum": "" + }, + "require": { + "behat/gherkin": "~4.3", + "behat/transliterator": "~1.0", + "ext-mbstring": "*", + "php": ">=5.3.3", + "symfony/class-loader": "~2.1", + "symfony/config": "~2.3", + "symfony/console": "~2.1", + "symfony/dependency-injection": "~2.1", + "symfony/event-dispatcher": "~2.1", + "symfony/translation": "~2.1", + "symfony/yaml": "~2.1" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "~1.0", + "phpunit/phpunit": "~4.0.7", + "symfony/process": "~2.1" + }, + "suggest": { + "behat/mink-extension": "for integration with Mink testing framework", + "behat/symfony2-extension": "for integration with Symfony2 web framework", + "behat/yii-extension": "for integration with Yii web framework" + }, + "bin": [ + "bin/behat" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Behat": "src/", + "Behat\\Testwork": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Scenario-oriented BDD framework for PHP 5.3", + "homepage": "http://behat.org/", + "keywords": [ + "Agile", + "BDD", + "ScenarioBDD", + "Scrum", + "StoryBDD", + "User story", + "business", + "development", + "documentation", + "examples", + "symfony", + "testing" + ], + "time": "2014-06-20 21:35:01" + }, + { + "name": "behat/gherkin", + "version": "v4.3.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "43777c51058b77bcd84a8775b7a6ad05e986b5db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/43777c51058b77bcd84a8775b7a6ad05e986b5db", + "reference": "43777c51058b77bcd84a8775b7a6ad05e986b5db", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/yaml": "~2.1" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP 5.3", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "time": "2014-06-06 01:24:32" + }, + { + "name": "behat/transliterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "c93521d3462a554332d1ef5bb0e9b5b8ca4106c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/c93521d3462a554332d1ef5bb0e9b5b8ca4106c4", + "reference": "c93521d3462a554332d1ef5bb0e9b5b8ca4106c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "time": "2014-05-15 22:08:22" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "07b195e1eee19ee99cd9432983e1fb39adacdde1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/07b195e1eee19ee99cd9432983e1fb39adacdde1", + "reference": "07b195e1eee19ee99cd9432983e1fb39adacdde1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*@stable" + }, + "suggest": { + "dflydev/markdown": "1.0.*", + "erusev/parsedown": "~0.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2014-04-26 10:10:44" + }, + { + "name": "phpspec/php-diff", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/phpspec/php-diff.git", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Diff": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Chris Boulton", + "homepage": "http://github.com/chrisboulton", + "role": "Original developer" + } + ], + "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", + "time": "2013-11-01 13:02:21" + }, + { + "name": "phpspec/phpspec", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/phpspec.git", + "reference": "a65fb9da78a988b5f26dce56ab58b5f44cc492de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/a65fb9da78a988b5f26dce56ab58b5f44cc492de", + "reference": "a65fb9da78a988b5f26dce56ab58b5f44cc492de", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpspec/php-diff": "~1.0.0", + "phpspec/prophecy": "~1.1", + "symfony/console": "~2.1", + "symfony/event-dispatcher": "~2.1", + "symfony/finder": "~2.1", + "symfony/yaml": "~2.1" + }, + "require-dev": { + "behat/behat": "~2.5", + "bossa/phpspec2-expect": "dev-master", + "symfony/filesystem": "~2.1" + }, + "suggest": { + "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" + }, + "bin": [ + "bin/phpspec" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "PhpSpec": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "homepage": "http://marcelloduarte.net/" + } + ], + "description": "Specification-oriented BDD framework for PHP 5.3+", + "homepage": "http://phpspec.net/", + "keywords": [ + "BDD", + "SpecBDD", + "TDD", + "spec", + "specification", + "testing", + "tests" + ], + "time": "2014-06-26 14:25:58" + }, + { + "name": "phpspec/prophecy", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "c9a80f8c324df415fea36ab1148a44ed6b456210" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9a80f8c324df415fea36ab1148a44ed6b456210", + "reference": "c9a80f8c324df415fea36ab1148a44ed6b456210", + "shasum": "" + }, + "require": { + "phpdocumentor/reflection-docblock": "~2.0" + }, + "require-dev": { + "phpspec/phpspec": "2.0.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "http://phpspec.org", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2014-06-14 19:28:44" + }, + { + "name": "symfony/class-loader", + "version": "dev-master", + "target-dir": "Symfony/Component/ClassLoader", + "source": { + "type": "git", + "url": "https://github.com/symfony/ClassLoader.git", + "reference": "9872f5b7a9547b3c4c7474cb4d5d4eb4cad3b4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/9872f5b7a9547b3c4c7474cb4d5d4eb4cad3b4a5", + "reference": "9872f5b7a9547b3c4c7474cb4d5d4eb4cad3b4a5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/finder": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\ClassLoader\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "http://symfony.com", + "time": "2014-05-23 14:36:49" + }, + { + "name": "symfony/config", + "version": "dev-master", + "target-dir": "Symfony/Component/Config", + "source": { + "type": "git", + "url": "https://github.com/symfony/Config.git", + "reference": "32018f35de323afd1bffb601f9e760ff3fb6ba05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Config/zipball/32018f35de323afd1bffb601f9e760ff3fb6ba05", + "reference": "32018f35de323afd1bffb601f9e760ff3fb6ba05", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/filesystem": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Config\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "http://symfony.com", + "time": "2014-05-23 14:36:49" + }, + { + "name": "symfony/dependency-injection", + "version": "dev-master", + "target-dir": "Symfony/Component/DependencyInjection", + "source": { + "type": "git", + "url": "https://github.com/symfony/DependencyInjection.git", + "reference": "4040578469625dc4d3c3c70f583bb995c764c434" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/4040578469625dc4d3c3c70f583bb995c764c434", + "reference": "4040578469625dc4d3c3c70f583bb995c764c434", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.2", + "symfony/expression-language": "~2.4", + "symfony/yaml": "~2.0" + }, + "suggest": { + "symfony/config": "", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DependencyInjection\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "http://symfony.com", + "time": "2014-05-23 14:36:49" + }, + { + "name": "symfony/event-dispatcher", + "version": "dev-master", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "7a4f4b42886f8c2e35acb1071f157a0b8eb1bd39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/7a4f4b42886f8c2e35acb1071f157a0b8eb1bd39", + "reference": "7a4f4b42886f8c2e35acb1071f157a0b8eb1bd39", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0", + "symfony/dependency-injection": "~2.0", + "symfony/stopwatch": "~2.2" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2014-05-23 14:36:49" + }, + { + "name": "symfony/filesystem", + "version": "dev-master", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "c5535943b0d48e5cc54f5bc8088bfc0aefda7f39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/c5535943b0d48e5cc54f5bc8088bfc0aefda7f39", + "reference": "c5535943b0d48e5cc54f5bc8088bfc0aefda7f39", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2014-06-20 17:41:51" + }, + { + "name": "symfony/finder", + "version": "dev-master", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "bed12a8b775813b252daf137b64f79113fbc5da5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/bed12a8b775813b252daf137b64f79113fbc5da5", + "reference": "bed12a8b775813b252daf137b64f79113fbc5da5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2014-05-23 14:36:49" + }, + { + "name": "symfony/translation", + "version": "dev-master", + "target-dir": "Symfony/Component/Translation", + "source": { + "type": "git", + "url": "https://github.com/symfony/Translation.git", + "reference": "648fe60e8921358aadb0d50b74339c52691a1763" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Translation/zipball/648fe60e8921358aadb0d50b74339c52691a1763", + "reference": "648fe60e8921358aadb0d50b74339c52691a1763", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.0", + "symfony/yaml": "~2.2" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "http://symfony.com", + "time": "2014-06-17 05:29:56" + }, + { + "name": "symfony/yaml", + "version": "dev-master", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "b01d366060f33e18fd98b0008c41a01b0d25c95c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/b01d366060f33e18fd98b0008c41a01b0d25c95c", + "reference": "b01d366060f33e18fd98b0008c41a01b0d25c95c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-06-04 06:43:44" + } + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "symfony/console": 20, + "phpspec/phpspec": 20, + "behat/behat": 20 + }, + "platform": [ + + ], + "platform-dev": [ + + ] +} diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php new file mode 100644 index 0000000..cc265eb --- /dev/null +++ b/features/bootstrap/FeatureContext.php @@ -0,0 +1,80 @@ +binFolder = __DIR__ . '/../../bin/'; + $this->output = ''; + } + + /** + * @When I run :command + */ + public function iRun($command) + { + $poser = $this->binFolder . $command; + $this->return = -1; + 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) + { + $filePath = __DIR__.'/../'.$filePath; + $content = file_get_contents($filePath); + + $this->assertEquals($content, $this->output); + } + /** + * @Then it should pass + */ + public function itShouldPass() + { + if (0 != $this->return) { + throw new \Exception('Error executing '.$this->return); + } + } + + /** + * @Then the content of :given should be equal to :expected + */ + public function theContentOfShouldBeEqualTo($given, $expected) + { + $givenPath = $given; + $given = file_get_contents($givenPath); + + $expectedPath = __DIR__.'/../'.$expected; + $expected = file_get_contents($expectedPath); + unlink($givenPath); + + $this->assertEquals($given, $expected); + } + + private function assertEquals($given, $expected) + { + $expected = preg_replace('/\s+/', '', $expected); + $given = preg_replace('/\s+/', '', $given); + + if ($expected != $given) { + throw new \Exception('Expected:'.$expected.'\n given:'.$given); + } + } +} diff --git a/features/bootstrap/fixtures/license-MIT-blue.svg b/features/bootstrap/fixtures/license-MIT-blue.svg new file mode 100644 index 0000000..5d9d856 --- /dev/null +++ b/features/bootstrap/fixtures/license-MIT-blue.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + license + license + MIT + MIT + + \ No newline at end of file diff --git a/features/ui_command_creating_image_file.feature b/features/ui_command_creating_image_file.feature new file mode 100644 index 0000000..1b50c3a --- /dev/null +++ b/features/ui_command_creating_image_file.feature @@ -0,0 +1,10 @@ +@ui +Feature: Generation of an image + In order to create image by command line + As a visitor + I want to use the poser script + + Scenario: Create the image running a script + When I run "poser license MIT blue -p /tmp/img.svg" + Then it should pass + And the content of "/tmp/img.svg" should be equal to "bootstrap/fixtures/license-MIT-blue.svg" \ No newline at end of file diff --git a/features/ui_command_echo_image.feature b/features/ui_command_echo_image.feature new file mode 100644 index 0000000..f27df3b --- /dev/null +++ b/features/ui_command_echo_image.feature @@ -0,0 +1,10 @@ +@ui +Feature: Generation of an image by echo-ing the content + In order to create image by command line + As a visitor + I want to use the poser script + + Scenario: Echo the image running a script + When I run "poser license MIT blue" + Then it should pass + And the same output should be like the content of "bootstrap/fixtures/license-MIT-blue.svg" \ No newline at end of file diff --git a/i_m-badge.svg b/i_m-badge.svg new file mode 100644 index 0000000..b7e984d --- /dev/null +++ b/i_m-badge.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + I m a + I m a + badge + badge + + \ No newline at end of file diff --git a/spec/Fixtures/license.svg b/spec/Fixtures/license.svg new file mode 100644 index 0000000..df4ea43 --- /dev/null +++ b/spec/Fixtures/license.svg @@ -0,0 +1,13 @@ + + + + + + + +stable +stable +v2.0 +v2.0 + + \ No newline at end of file diff --git a/spec/PUGX/Poser/BadgeSpec.php b/spec/PUGX/Poser/BadgeSpec.php new file mode 100644 index 0000000..8a48662 --- /dev/null +++ b/spec/PUGX/Poser/BadgeSpec.php @@ -0,0 +1,63 @@ +beConstructedWith('a','b','97CA00', 'svg'); + $this->shouldHaveType('Pugx\Poser\Badge'); + } + + function it_should_be_constructed_by_fromURI_factory_method() + { + $this->beConstructedWith('a','b','97CA00', 'svg'); + $assert = 'version-stable-97CA00.svg'; + $it = \PUGX\Poser\Badge::fromURI($assert); + + if ((string) $it !== $assert) { + throw new Exception(sprintf("from[%s] having[%s]\n", $assert, (string) $it)); + } + } + + function it_should_be_constructed_by_fromURI_factory_method_escaping_correctly_underscores() + { + $this->beConstructedWith('a','b','97CA00', 'svg'); + $input = 'I__m__liugg__io-b-97CA00.svg'; + $assertInput = 'I_m_liugg_io-b-97CA00.svg'; + $it = \PUGX\Poser\Badge::fromURI($input); + + if ((string) $it !== $assertInput) { + 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() + { + $this->beConstructedWith('a','b','97CA00', 'svg'); + $input = 'I_m_liuggio-b-97CA00.svg'; + $assertInput = 'I m liuggio-b-97CA00.svg'; + $it = \PUGX\Poser\Badge::fromURI($input); + + if ((string) $it !== $assertInput) { + 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() + { + $this->beConstructedWith('a','b','97CA00', 'svg'); + $input = 'I--m--liuggio-b-97CA00.svg'; + $assertInput = 'I-m-liuggio-b-97CA00.svg'; + $it = \PUGX\Poser\Badge::fromURI($input); + + if ((string) $it !== $assertInput) { + throw new Exception(sprintf("from[%s] wants[%s] having[%s]\n", $input, $assertInput, (string) $it)); + } + } +} diff --git a/spec/PUGX/Poser/PoserSpec.php b/spec/PUGX/Poser/PoserSpec.php new file mode 100644 index 0000000..d578933 --- /dev/null +++ b/spec/PUGX/Poser/PoserSpec.php @@ -0,0 +1,59 @@ +beConstructedWith(array('svg' => $render)); + } + + function it_is_initializable() + { + $this->shouldHaveType('PUGX\Poser\Poser'); + } + + function it_should_be_able_to_generate_an_svg_image() + { + $subject = 'stable'; + $status = 'v2.0'; + $color = '97CA00'; + $format = 'svg'; + + $this->generate($subject, $status, $color, $format)->shouldBeAValidSVGImageContaining($subject, $status); + } + + function it_should_be_able_to_generate_an_svg_image_from_URI() + { + $subject = 'stable-v2.0-97CA00.svg'; + + $this->generateFromURI($subject)->shouldBeAValidSVGImageContaining('stable', 'v2.0'); + } + + + public function getMatchers() + { + return [ + 'beAValidSVGImage' => function($subject) { + + $regex = '/^$/'; + $matches = array(); + + return preg_match($regex, $subject, $matches, PREG_OFFSET_CAPTURE, 0); + }, + 'beAValidSVGImageContaining' => function($object, $subject, $status) { + + $regex = '/^$/'; + $matches = array(); + + return preg_match($regex, $object, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; + } +} diff --git a/spec/PUGX/Poser/Render/SvgRenderSpec.php b/spec/PUGX/Poser/Render/SvgRenderSpec.php new file mode 100644 index 0000000..f7f9999 --- /dev/null +++ b/spec/PUGX/Poser/Render/SvgRenderSpec.php @@ -0,0 +1,44 @@ +calculateWidth(Argument::any())->willReturn(20); + $this->beConstructedWith($calculator); + } + + function it_should_render_a_svg() + { + $badge = Badge::fromURI('version-stable-97CA00.svg'); + $this->render($badge)->shouldBeAValidSVGImage(); + } + + public function getMatchers() + { + return [ + 'beAValidSVGImage' => function($subject) { + + $regex = '/^$/'; + $matches = array(); + + return preg_match($regex, $subject, $matches, PREG_OFFSET_CAPTURE, 0); + }, + 'beAValidSVGImageContaining' => function($object, $subject, $status) { + + $regex = '/^$/'; + $matches = array(); + + return preg_match($regex, $object, $matches, PREG_OFFSET_CAPTURE, 0); + }, + ]; + } +} \ No newline at end of file diff --git a/src/Badge.php b/src/Badge.php new file mode 100644 index 0000000..8c502bd --- /dev/null +++ b/src/Badge.php @@ -0,0 +1,150 @@ + "4c1", + "green" => "97CA00", + "yellow" => "dfb317", + "yellowgreen" => "a4a61d", + "orange" => "fe7d37", + "red" => "e05d44", + "blue" => "007ec6", + "grey" => "555", + "lightgray" => "9f9f9f" + ); + + private $subject; + private $status; + private $color; + private $format; + + public function __construct($subject, $status, $color, $format = self::DEFAULT_FORMAT) + { + $this->subject = $this->escapeValue($subject); + $this->status = $this->escapeValue($status); + $this->format = $this->escapeValue($format); + $this->color = $this->getColorHex($color); + + if (!$this->isValidColorHex($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() + { + return array_keys(self::$colorScheme); + } + + /** + * Factory method the creates a Badge from an URI + * eg. I_m-liuggio-yellow.svg + * + * @param string $URI + * + * @return Badge + * @throws \InvalidArgumentException + */ + public static function fromURI($URI) + { + $regex = '/^(([^-]|--)+)-(([^-]|--)+)-(([^-]|--)+)\.(svg|png|gif|jpg)$/'; + $match = array(); + + if (1 != preg_match($regex, $URI, $match) && (7 != 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]; + + return new self($subject, $status, $color, $format); + } + + /** + * @return string the Hexadecimal #FFFFFF. + */ + public function getHexColor() + { + return '#'.$this->color; + } + + /** + * @return string the format of the image eg. `svg`. + */ + public function getFormat() + { + return $this->format; + } + + /** + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * @return string + */ + public function getSubject() + { + return $this->subject; + } + + public function __toString() + { + return sprintf("%s-%s-%s.%s", + $this->subject, + $this->status, + $this->color, + $this->format + ); + } + + private function escapeValue($value) + { + $pattern = array( + // '/([^_])_([^_])/g', // damn it global doesn't work in PHP + '/([^_])_$/', + '/^_([^_])/', + '/__/', + '/--+/', + ); + $replacement = array( + //'$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 :( + + return $ret; + } + + private function getColorHex($color) + { + return array_key_exists($color, self::$colorScheme) ? self::$colorScheme[$color] : $color; + } + + private function isValidColorHex($color) + { + $color = ltrim($color, "#"); + $regex = '/^[0-9a-fA-F]{6}$/'; + + return preg_match($regex, $color); + } +} diff --git a/src/Calculator/Font/DejaVuSans.ttf b/src/Calculator/Font/DejaVuSans.ttf new file mode 100755 index 0000000..2db9bde Binary files /dev/null and b/src/Calculator/Font/DejaVuSans.ttf differ diff --git a/src/Calculator/GDTextSizeCalculator.php b/src/Calculator/GDTextSizeCalculator.php new file mode 100644 index 0000000..84adb47 --- /dev/null +++ b/src/Calculator/GDTextSizeCalculator.php @@ -0,0 +1,42 @@ + + * + * 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 function __construct() + { + $this->fontPath = __DIR__ . self::TEXT_FONT; + } + + /** + * Calculate the width of the text box. + * + * @param string $text + * @param int $size + * + * @return float + */ + public function calculateWidth($text, $size = self::TEXT_SIZE) + { + $size = $this->convertToPt($size); + $box = imagettfbbox($size, 0, $this->fontPath, $text); + + return round(abs($box[2] - $box[0]) + self::SHIELD_PADDING_EXTERNAL + self::SHIELD_PADDING_INTERNAL, 1); + } + + private function convertToPt($pixels) + { + return round($pixels * 0.75, 1); + } +} diff --git a/src/Calculator/TextSizeCalculatorInterface.php b/src/Calculator/TextSizeCalculatorInterface.php new file mode 100644 index 0000000..ae79caa --- /dev/null +++ b/src/Calculator/TextSizeCalculatorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PUGX\Poser\Calculator; + +interface TextSizeCalculatorInterface +{ + const TEXT_SIZE = 11; + const SHIELD_PADDING_EXTERNAL = 6; + 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); +} diff --git a/src/Poser.php b/src/Poser.php new file mode 100644 index 0000000..de4261c --- /dev/null +++ b/src/Poser.php @@ -0,0 +1,79 @@ +renders = array(); + + foreach ($renders as $format => $render) { + $this->addFormatRender($format, $render); + } + } + + /** + * Generate and Render a badge according to the format. + * + * @param $subject + * @param $status + * @param $color + * @param $format + * + * @return string + */ + public function generate($subject, $status, $color, $format) + { + $badge = new Badge($subject, $status, $color, $format); + + return $this->getRenderFor($badge->getFormat())->render($badge); + } + + /** + * Generate and Render a badge according to the format from an URI, + * eg license-MIT-blue.svg or I_m-liuggio-yellow.svg. + * + * @param $string + * @return mixed + */ + public function generateFromURI($string) + { + $badge = Badge::fromURI($string); + + return $this->getRenderFor($badge->getFormat())->render($badge); + } + + /** + * All the formats available. + * + * @return array + */ + public function validFormats() + { + return array_keys($this->renders); + } + + private function addFormatRender($format, RenderInterface $render) + { + $this->renders[$format] = $render; + } + + private function getRenderFor($format) + { + if (!isset($this->renders[$format])) { + throw new \InvalidArgumentException(sprintf('No render founds for this format [%s]', $format)); + } + + return $this->renders[$format]; + } +} diff --git a/src/Render/RenderInterface.php b/src/Render/RenderInterface.php new file mode 100644 index 0000000..65f18d2 --- /dev/null +++ b/src/Render/RenderInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PUGX\Poser\Render; + +use PUGX\Poser\Badge; + +interface RenderInterface +{ + public function render(Badge $badge); +} diff --git a/src/Render/SvgRender.php b/src/Render/SvgRender.php new file mode 100644 index 0000000..2407ecc --- /dev/null +++ b/src/Render/SvgRender.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PUGX\Poser\Render; + +use PUGX\Poser\Badge; +use PUGX\Poser\Calculator\TextSizeCalculatorInterface; +use PUGX\Poser\Calculator\GDTextSizeCalculator; + +/** + * Class SvgGenerator + * + * @author Claudio D'Alicandro + * @author Giulio De Donato + */ +class SvgRender implements RenderInterface +{ + const VENDOR_COLOR = '#555'; + + private $textSizeCalculator; + private static $template = ' + + + + + + + + + + + + {{ vendor }} + {{ vendor }} + {{ value }} + {{ value }} + +'; + + /** + * Constructor. + * + * @param TextSizeCalculatorInterface $textSizeCalculator + */ + public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) + { + $this->textSizeCalculator = $textSizeCalculator; + + if (null === $this->textSizeCalculator) { + $this->textSizeCalculator = new GDTextSizeCalculator(); + } + } + + /** + * @param Badge $badge + * + * @return mixed + */ + public function render(Badge $badge) + { + $parameters = array(); + + $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); + $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); + $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; + $parameters['vendorColor'] = self::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 $this->renderSvg(self::$template, $parameters); + } + + private function stringWidth($text) + { + return $this->textSizeCalculator->calculateWidth($text); + } + + private function renderSvg($render, $parameters) + { + foreach ($parameters as $key => $variable) { + $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); + } + + return $render; + } +} diff --git a/src/UI/Command.php b/src/UI/Command.php new file mode 100644 index 0000000..e2fd60a --- /dev/null +++ b/src/UI/Command.php @@ -0,0 +1,128 @@ + |_ _ _| _ _ _ _ _ _ _ | + |_)(_|(_|(_|(/_ |_)(_)_\(/_| | + _| _|______________| + + http://poser.pug.org +"; + + private $poser; + + private function init() + { + $this->poser = new Poser(array('svg' => new SvgRender())); + $this->format = 'svg'; + $this->header = self::HEADER; + } + + protected function configure() + { + $this->init(); + + $this + ->setName('generate') + ->setDescription('Create a badge you are a Poser.') + ->addArgument( + 'subject', + InputArgument::OPTIONAL, + 'The subject eg. `license`' + ) + ->addArgument( + 'status', + InputArgument::OPTIONAL, + 'The status example `MIT`' + ) + ->addArgument( + 'color', + InputArgument::OPTIONAL, + 'The hexadecimal color eg. `97CA00` or the name ['.join(', ', Badge::getColorNamesAvailable()).']' + ) + ->addOption( + 'format', + 'f', + InputOption::VALUE_REQUIRED, + 'The format of the image eg. `svg`, formats available ['.join(', ', $this->poser->validFormats()).']' + ) + ->addOption( + 'path', + 'p', + InputOption::VALUE_REQUIRED, + 'The path of the file to save the create eg. `/tmp/license.svg`' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + + $subject = $input->getArgument('subject'); + $status = $input->getArgument('status'); + $color = $input->getArgument('color'); + + if ($input->getOption('format')) { + $this->format = $input->getOption('format'); + } + + try { + $imageContent = $this->poser->generate($subject, $status, $color, $this->format); + + if ($input->getOption('path')) { + $this->storeImage($input, $output, $input->getOption('path'), $imageContent); + } else { + $this->flushImage($input, $output, $imageContent); + } + + } catch (\Exception $e) { + $this->printHeaderOnce($output); + throw $e; + } + } + + protected function flushImage(InputInterface $input, OutputInterface $output, $imageContent) + { + $output->write($imageContent); + $this->header = ''; + } + + protected function storeImage(InputInterface $input, OutputInterface $output, $path, $imageContent) + { + $this->printHeaderOnce($output); + $fp = fopen($path,"x"); // if file already exists warning is raised + + 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)) { + throw new \Exception('Error on writing to file.'); + } + fclose($fp); + + $output->write(sprintf('Image created at %s', $path)); + } + + protected function printHeaderOnce(OutputInterface $output) + { + if (!empty($this->header)) { + $output->write($this->header); + } + + $this->header = ''; + } + +} diff --git a/src/UI/SingleCommandApplication.php b/src/UI/SingleCommandApplication.php new file mode 100644 index 0000000..545977f --- /dev/null +++ b/src/UI/SingleCommandApplication.php @@ -0,0 +1,87 @@ +run(); + * + * @author Stefaan Lippens + */ +class SingleCommandApplication extends Application +{ + /** + * Name of the single accessible command of this application + * @var string + */ + private $commandName; + + /** + * Constructor to build a "single command" application, given a command. + * + * The application will adopt the same name as the command. + * + * @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') + { + parent::__construct($command->getName(), $version); + + // Add the given command as single (accessible) command. + $this->add($command); + $this->commandName = $command->getName(); + + // Override the Application's definition so that it does not + // require a command name as first argument. + $this->getDefinition()->setArguments(); + } + + /** + * {@inheritdoc} + */ + protected function getCommandName(InputInterface $input) + { + return $this->commandName; + } + + /** + * Adds a command object. + * + * This function overrides (public) Application::add() + * but should should only be used internally. + * Will raise \LogicException when called + * after the single accessible command is set up + * (from the constructor). + * + * @param Command $command A Command object + * + * @return Command The registered command + * + * @throws \LogicException + */ + public function add(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."); + } + + return parent::add($command); + } +} diff --git a/today-dark.svg b/today-dark.svg new file mode 100644 index 0000000..0ca6d85 --- /dev/null +++ b/today-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + today + today + dark + dark + + \ No newline at end of file