diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35ab0d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Third party +/composer.lock +/phpunit.xml +/vendor +/.couscous +/.puli diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c955756 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: +# include: +# - php: 5.3.3 +# env: COMPOSER_FLAGS="--prefer-lowest" + allow_failures: + - php: hhvm + - php: 7.0 + +before_script: + - curl http://cs.sensiolabs.org/get/php-cs-fixer.phar -o php-cs-fixer.phar + - composer selfupdate + - composer update $COMPOSER_FLAGS + +script: + - echo '[phpspec] Running specification tests'; ./vendor/bin/phpspec run -n -f dot + - output=$(php -n php-cs-fixer.phar fix -v --dry-run --config=sf23 .); if [[ $(grep -o F <<< $output | wc -l) -gt 3 ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi; diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..965706e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG + +## 1.0.0-rc1: Import + +* imported twig template engine from [memio/pretty-printer](http://github.com/memio/pretty-printer) v1.0.0-rc5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b705730 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# How to contribute + +Everybody should be able to help. Here's how you can make this project more +awesome: + +1. [Fork it](https://github.com/memio/twig-template-engine/fork_select) +2. improve it +3. submit a [pull request](https://help.github.com/articles/creating-a-pull-request) + +Your work will then be reviewed as soon as possible (suggestions about some +changes, improvements or alternatives may be given). + +Here's some tips to make you the best contributor ever: + +* [Standard code](#standard-code) +* [Specifications](#specifications) +* [Keeping your fork up-to-date](#keeping-your-fork-up-to-date) + +## Standard code + +Use [PHP CS fixer](http://cs.sensiolabs.org/) to make your code compliant with +Memio TwigTemplateEngine's coding standards: + + ./vendor/bin/php-cs-fixer fix --config=sf23 . + +## Specifications + +Memio TwigTemplateEngine drives its development using [phpspec](http://www.phpspec.net/): + + # Generate the specification class: + phpspec describe 'Memio\Memio\MyNewClass' + + # Customize the specification class: + $EDITOR tests/spec/Memio/TwigTemplateEngine/MyNewClass.php + + # Generate the specified class: + phpspec run + + # Customize the class: + $EDITOR src/Memio/TwigTemplateEngine/MyNewClass.php + + phpspec run # Should be green! + +## Keeping your fork up-to-date + +To keep your fork up-to-date, you should track the upstream (original) one +using the following command: + + git remote add upstream https://github.com/memio/twig-template-engine.git + +Then get the upstream changes: + + git checkout master + git pull --rebase origin master + git pull --rebase upstream master + git checkout + git rebase master + +Finally, publish your changes: + + git push -f origin + +Your pull request will be automatically updated. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba0bc4d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Loïc Chardonnet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c92d361 --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# Memio's TwigTemplateEngine [![SensioLabsInsight](https://insight.sensiolabs.com/projects/c8194cd1-0f80-4bce-9ab2-8368db5411b3/mini.png)](https://insight.sensiolabs.com/projects/c8194cd1-0f80-4bce-9ab2-8368db5411b3) [![Travis CI](https://travis-ci.org/memio/twig-template-engine.png)](https://travis-ci.org/memio/twig-template-engine) + +`PrettyPrinter` is a code generator (printer) that takes a Model and calls the +appropriate `TemplateEngine` to actually generate the corresponding code, +using highly opinionated coding standards (pretty). + +`PrettyPrinter` returns a string that can be saved in a file, dislpayed on a +console output or displayed in a web page. Possibilities are endless! + +> **Note**: This package is part of [Memio](http://memio.github.io/memio). +> Have a look at [the main repository](http://github.com/memio/memio). + +## Installation + +Install it using [Composer](https://getcomposer.org/download): + + composer require memio/twig-template-engine:~1.0@rc + +## Example + +We're going to generate a class with a constructor and two attributes: + +```php +add(new ContractLineStrategy()); +$line->add(new FileLineStrategy()); +$line->add(new MethodPhpdocLineStrategy()); +$line->add(new ObjectLineStrategy()); +$line->add(new StructurePhpdocLineStrategy()); + +$twig->addExtension(new Type()); +$twig->addExtension(new Whitespace($line)); + +$templateEngine = new TwigTemplateEngine($twig); +$prettyPrinter = new PrettyPrinter($templateEngine); + +// Describe the code you want to generate using "Models" +$myService = File::make('src/Vendor/Project/MyService.php') + ->setStructure( + Object::make('Vendor\Project\MyService') + ->addProperty(new Property('createdAt')) + ->addProperty(new Property('filename')) + ->addMethod( + Method::make('__construct') + ->addArgument(new Argument('DateTime', 'createdAt')) + ->addArgument(new Argument('string', 'filename')) + ) + ) +; + +// Generate the code and display in the console +echo $prettyPrinter->generateCode($myService); + +// Or display it in a browser +// echo '
'.htmlspecialchars($prettyPrinter->generateCode($myService)).'
'; +``` + +With this simple example, we get the following output: + +``` + Given a version number MAJOR.MINOR.PATCH, increment the: +> +> 1. MAJOR version when you make incompatible API changes +> 2. MINOR version when you add functionality in a backwards-compatible manner +> 3. PATCH version when you make backwards-compatible bug fixes + +### Public API + +Classes and methods marked with the `@api` tag are considered to be the public +API of this project. + +## Branching Model + +The branching is inspired by [@jbenet](https://github.com/jbenet) +[simple git branching model](https://gist.github.com/jbenet/ee6c9ac48068889b0912): + +> 1. `master` must always be deployable. +> 2. **all changes** are made through feature branches (pull-request + merge) +> 3. rebase to avoid/resolve conflicts; merge in to `master` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..17214f2 --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "memio/twig-template-engine", + "license": "MIT", + "type": "library", + "description": "Memio's Twig templates, used to generate PHP code from given Model", + "keywords": ["generator", "PHP", "code", "twig", "templates"], + "homepage": "http://memio.github.io/memio", + "authors": [ + { + "name": "Loïc Chardonnet", + "email": "loic.chardonnet@gmail.com", + "homepage": "http://gnugat.github.io", + "role": "Developer" + } + ], + "autoload": { "psr-4": { + "Memio\\TwigTemplateEngine\\": "src/Memio/TwigTemplateEngine", + "Memio\\TwigTemplateEngine\\Config\\": "config" + }}, + "require": { + "memio/model": "~1.0", + "memio/pretty-printer": "~1.0@rc", + "php": ">=5.3.3", + "twig/twig": "~1.18" + }, + "require-dev": { + "ciaranmcnulty/phpspec-typehintedmethods": "~1.1", + "fabpot/php-cs-fixer": "~1.6", + "phpspec/phpspec": "~2.2" + } +} diff --git a/config/Locate.php b/config/Locate.php new file mode 100644 index 0000000..e464bde --- /dev/null +++ b/config/Locate.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Contract; +use PhpSpec\ObjectBehavior; + +class ContractLineStrategySpec extends ObjectBehavior +{ + const CONSTANT_BLOCK = 'constants'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy'); + } + + function it_supports_contracts(Contract $contract) + { + $this->supports($contract)->shouldBe(true); + } + + function it_needs_line_after_constants_if_contract_has_both_constants_and_methods(Contract $contract) + { + $contract->allConstants()->willReturn(array(1)); + $contract->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($contract, self::CONSTANT_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategySpec.php b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategySpec.php new file mode 100644 index 0000000..59fd975 --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategySpec.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\File; +use PhpSpec\ObjectBehavior; + +class FileLineStrategySpec extends ObjectBehavior +{ + const IMPORT_BLOCK = 'fully_qualified_names'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy'); + } + + function it_supports_files(File $file) + { + $this->supports($file)->shouldBe(true); + } + + function it_needs_line_after_fully_qualified_names_if_file_has_fully_qualified_names(File $file) + { + $file->allFullyQualifiedNames()->willReturn(array(1)); + + $this->needsLineAfter($file, self::IMPORT_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigExtension/Line/LineSpec.php b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/LineSpec.php new file mode 100644 index 0000000..e45487c --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/LineSpec.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Argument; +use Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy; +use PhpSpec\ObjectBehavior; + +class LineSpec extends ObjectBehavior +{ + const BLOCK = 'constants'; + const STRATEGY_RETURN = true; + + function let(LineStrategy $lineStrategy) + { + $this->add($lineStrategy); + } + + function it_executes_the_first_strategy_that_supports_given_model(Argument $model, LineStrategy $lineStrategy) + { + $lineStrategy->supports($model)->willReturn(true); + $lineStrategy->needsLineAfter($model, self::BLOCK)->willReturn(self::STRATEGY_RETURN); + + $this->needsLineAfter($model, self::BLOCK)->shouldBe(self::STRATEGY_RETURN); + } + + function it_fails_when_no_strategy_supports_given_model(Argument $model, LineStrategy $lineStrategy) + { + $exception = 'Memio\PrettyPrinter\Exception\InvalidArgumentException'; + + $lineStrategy->supports($model)->willReturn(false); + + $this->shouldThrow($exception)->duringNeedsLineAfter($model, self::BLOCK); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategySpec.php b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategySpec.php new file mode 100644 index 0000000..8abeed0 --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategySpec.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Phpdoc\MethodPhpdoc; +use Memio\Model\Phpdoc\ApiTag; +use Memio\Model\Phpdoc\Description; +use Memio\Model\Phpdoc\DeprecationTag; +use Memio\Model\Phpdoc\ParameterTag; +use PhpSpec\ObjectBehavior; + +class MethodPhpdocLineStrategySpec extends ObjectBehavior +{ + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy'); + } + + function it_supports_method_phpdocs(MethodPhpdoc $methodPhpdoc) + { + $this->supports($methodPhpdoc)->shouldBe(true); + } + + function it_needs_line_after_description_if_it_has_any_other_tag( + Description $description, + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc + ) + { + $methodPhpdoc->getApiTag()->willReturn(null); + $methodPhpdoc->getDescription()->willReturn($description); + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getParameterTags()->willReturn(null); + + $this->needsLineAfter($methodPhpdoc, 'description')->shouldBe(true); + } + + function it_needs_line_after_parameter_tags_if_it_has_api_or_deprecation_tags( + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc, + ParameterTag $parameterTag + ) + { + $methodPhpdoc->getApiTag()->willReturn(null); + $methodPhpdoc->getDescription()->willReturn(null); + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getParameterTags()->willReturn(array($parameterTag)); + + $this->needsLineAfter($methodPhpdoc, 'parameter_tags')->shouldBe(true); + } + + function it_needs_line_after_deprecation_it_also_has_an_api_tag( + ApiTag $apiTag, + DeprecationTag $deprecationTag, + MethodPhpdoc $methodPhpdoc + ) + { + $methodPhpdoc->getDeprecationTag()->willReturn($deprecationTag); + $methodPhpdoc->getDescription()->willReturn(null); + $methodPhpdoc->getApiTag()->willReturn($apiTag); + $methodPhpdoc->getParameterTags()->willReturn(null); + + $this->needsLineAfter($methodPhpdoc, 'deprecation_tag')->shouldBe(true); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategySpec.php b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategySpec.php new file mode 100644 index 0000000..aa6218a --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategySpec.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Object; +use PhpSpec\ObjectBehavior; + +class ObjectLineStrategySpec extends ObjectBehavior +{ + const CONSTANT_BLOCK = 'constants'; + const PROPERTY_BLOCK = 'properties'; + + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy'); + } + + function it_supports_objects(Object $object) + { + $this->supports($object)->shouldBe(true); + } + + function it_needs_line_after_constants_if_object_has_both_constants_and_properties(Object $object) + { + $object->allConstants()->willReturn(array(1)); + $object->allProperties()->willReturn(array(2)); + $object->allMethods()->willReturn(array()); + + $this->needsLineAfter($object, self::CONSTANT_BLOCK)->shouldBe(true); + } + + function it_needs_line_after_constants_if_object_has_both_constants_and_methods(Object $object) + { + $object->allConstants()->willReturn(array(1)); + $object->allProperties()->willReturn(array()); + $object->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($object, self::CONSTANT_BLOCK)->shouldBe(true); + } + + function it_needs_line_after_properties_if_object_has_both_properties_and_methods(Object $object) + { + $object->allConstants()->willReturn(array()); + $object->allProperties()->willReturn(array(1)); + $object->allMethods()->willReturn(array(2)); + + $this->needsLineAfter($object, self::PROPERTY_BLOCK)->shouldBe(true); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategySpec.php b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategySpec.php new file mode 100644 index 0000000..a802307 --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategySpec.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Phpdoc\StructurePhpdoc; +use Memio\Model\Phpdoc\ApiTag; +use Memio\Model\Phpdoc\Description; +use Memio\Model\Phpdoc\DeprecationTag; +use PhpSpec\ObjectBehavior; + +class StructurePhpdocLineStrategySpec extends ObjectBehavior +{ + function it_is_a_line_strategy() + { + $this->shouldImplement('Memio\TwigTemplateEngine\TwigExtension\Line\LineStrategy'); + } + + function it_supports_structure_phpdocs(StructurePhpdoc $structurePhpdoc) + { + $this->supports($structurePhpdoc)->shouldBe(true); + } + + function it_needs_line_after_description_if_description_and_deprecation_or_api_are_defined( + ApiTag $apiTag, + Description $description, + DeprecationTag $deprecationTag, + StructurePhpdoc $structurePhpdoc + ) + { + $structurePhpdoc->getApiTag()->willReturn($apiTag); + $structurePhpdoc->getDescription()->willReturn($description); + $structurePhpdoc->getDeprecationTag()->willReturn($deprecationTag); + + $this->needsLineAfter($structurePhpdoc, 'description')->shouldBe(true); + } + + function it_needs_line_after_deprecation_if_deprecation_and_api_are_defined( + ApiTag $apiTag, + Description $description, + DeprecationTag $deprecationTag, + StructurePhpdoc $structurePhpdoc + ) + { + $structurePhpdoc->getApiTag()->willReturn($apiTag); + $structurePhpdoc->getDescription()->willReturn($description); + $structurePhpdoc->getDeprecationTag()->willReturn($deprecationTag); + + $this->needsLineAfter($structurePhpdoc, 'deprecation_tag')->shouldBe(true); + } +} diff --git a/spec/Memio/TwigTemplateEngine/TwigTemplateEngineSpec.php b/spec/Memio/TwigTemplateEngine/TwigTemplateEngineSpec.php new file mode 100644 index 0000000..8613a65 --- /dev/null +++ b/spec/Memio/TwigTemplateEngine/TwigTemplateEngineSpec.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace spec\Memio\TwigTemplateEngine; + +use PhpSpec\ObjectBehavior; +use Twig_Environment; +use Twig_Loader_Filesystem; + +class TwigTemplateEngineSpec extends ObjectBehavior +{ + const TEMPLATE_PATH = '/tmp/templates'; + const TEMPLATE = 'argument'; + const OUTPUT = '$dateTime'; + + function let(Twig_Environment $twig) + { + $this->beConstructedWith($twig); + } + + function it_is_a_template_engine() + { + $this->shouldHaveType('Memio\PrettyPrinter\TemplateEngine'); + } + + function it_can_have_more_paths(Twig_Environment $twig, Twig_Loader_Filesystem $loader) + { + $twig->getLoader()->willReturn($loader); + $loader->addPath(self::TEMPLATE_PATH)->shouldBeCalled(); + + $this->addPath(self::TEMPLATE_PATH); + } + + function it_renders_templates_using_twig(Twig_Environment $twig) + { + $parameters = array('name' => 'dateTime'); + + $twig->render(self::TEMPLATE.'.twig', $parameters)->willReturn(self::OUTPUT); + + $this->render(self::TEMPLATE, $parameters)->shouldBe(self::OUTPUT); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/ContractLineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/ContractLineStrategy.php new file mode 100644 index 0000000..54a5cbd --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/ContractLineStrategy.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\Contract; + +class ContractLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof Contract; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $constants = $model->allConstants(); + $methods = $model->allMethods(); + if ('constants' === $block) { + return (!empty($constants) && !empty($methods)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategy.php new file mode 100644 index 0000000..ce38461 --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/FileLineStrategy.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\File; + +class FileLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof File; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $fullyQualifiedNames = $model->allFullyQualifiedNames(); + if ('fully_qualified_names' === $block) { + return (!empty($fullyQualifiedNames)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/Line.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/Line.php new file mode 100644 index 0000000..a16169c --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/Line.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; + +class Line +{ + /** + * @var array + */ + private $strategies = array(); + + /** + * @param LineStrategy $lineStrategy + */ + public function add(LineStrategy $lineStrategy) + { + $this->strategies[] = $lineStrategy; + } + + /** + * @param mixed $model + * @param string $block + * + * @throws InvalidArgumentException If no strategy supports the given model + */ + public function needsLineAfter($model, $block) + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports($model)) { + return $strategy->needsLineAfter($model, $block); + } + } + + throw new InvalidArgumentException('No strategy supports given model '.get_class($model)); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/LineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/LineStrategy.php new file mode 100644 index 0000000..6140e2a --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/LineStrategy.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +interface LineStrategy +{ + /** + * @param mixed $model + * + * @return bool + */ + public function supports($model); + + /** + * @param mixed $model + * @param string $block + * + * @throws \Memio\PrettyPrinter\Exception\InvalidArgumentException If the block isn't supported + */ + public function needsLineAfter($model, $block); +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategy.php new file mode 100644 index 0000000..2c10d90 --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/MethodPhpdocLineStrategy.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Phpdoc\MethodPhpdoc; + +class MethodPhpdocLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof MethodPhpdoc; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $parameterTags = $model->getParameterTags(); + + $hasApiTag = (null !== $model->getApiTag()); + $hasParameterTags = (!empty($parameterTags)); + $hasDescription = (null !== $model->getDescription()); + $hasDeprecationTag = (null !== $model->getDeprecationTag()); + if ('description' === $block) { + return ($hasDescription && ($hasParameterTags || $hasApiTag || $hasDeprecationTag)); + } + if ('parameter_tags' === $block) { + return ($hasParameterTags && ($hasApiTag || $hasDeprecationTag)); + } + if ('deprecation_tag' === $block) { + return ($hasApiTag && $hasDeprecationTag); + } + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategy.php new file mode 100644 index 0000000..b95e410 --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/ObjectLineStrategy.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\PrettyPrinter\Exception\InvalidArgumentException; +use Memio\Model\Object; + +class ObjectLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof Object; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $constants = $model->allConstants(); + $properties = $model->allProperties(); + $methods = $model->allMethods(); + if ('constants' === $block) { + return (!empty($constants) && (!empty($properties) || !empty($methods))); + } + if ('properties' === $block) { + return (!empty($properties) && !empty($methods)); + } + + throw new InvalidArgumentException('The function needs_line_after does not support given "'.$block.'"'); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategy.php b/src/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategy.php new file mode 100644 index 0000000..e67e667 --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Line/StructurePhpdocLineStrategy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension\Line; + +use Memio\Model\Phpdoc\StructurePhpdoc; + +class StructurePhpdocLineStrategy implements LineStrategy +{ + /** + * {@inheritDoc} + */ + public function supports($model) + { + return $model instanceof StructurePhpdoc; + } + + /** + * {@inheritDoc} + */ + public function needsLineAfter($model, $block) + { + $hasDescription = (null !== $model->getDescription()); + $hasApiTag = (null !== $model->getApiTag()); + $hasDeprecationTag = (null !== $model->getDeprecationTag()); + if ('description' === $block) { + return ($hasDescription && ($hasApiTag || $hasDeprecationTag)); + } + if ('deprecation_tag' === $block) { + return ($hasApiTag && $hasDeprecationTag); + } + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Type.php b/src/Memio/TwigTemplateEngine/TwigExtension/Type.php new file mode 100644 index 0000000..a35a97c --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Type.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension; + +use Memio\Model\Argument; +use Memio\Model\Contract; +use Memio\Model\Type as ModelType; +use Twig_Extension; +use Twig_SimpleTest; +use Twig_SimpleFunction; + +class Type extends Twig_Extension +{ + /** + * {@inheritDoc} + */ + public function getTests() + { + return array( + new Twig_SimpleTest('contract', array($this, 'isContract')), + ); + } + + /** + * {@inheritDoc} + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('has_typehint', array($this, 'hasTypehint')), + ); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'type'; + } + + /** + * @param mixed $model + * + * @return bool + */ + public function isContract($model) + { + return $model instanceof Contract; + } + + /** + * @param mixed $model + * + * @return bool + */ + public function hasTypehint($model) + { + if (!$model instanceof Argument) { + return false; + } + $type = new ModelType($model->getType()); + + return $type->hasTypehint(); + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigExtension/Whitespace.php b/src/Memio/TwigTemplateEngine/TwigExtension/Whitespace.php new file mode 100644 index 0000000..774e6ca --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigExtension/Whitespace.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine\TwigExtension; + +use Memio\TwigTemplateEngine\TwigExtension\Line\Line; +use Twig_Extension; +use Twig_SimpleFilter; +use Twig_SimpleFunction; + +class Whitespace extends Twig_Extension +{ + /** + * @var Line + */ + private $line; + + /** + * @param Line $line + */ + public function __construct(Line $line) + { + $this->line = $line; + } + + /** + * {@inheritDoc} + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('needs_line_after', array($this->line, 'needsLineAfter')), + ); + } + + /** + * {@inheritDoc} + */ + public function getFilters() + { + return array( + new Twig_SimpleFilter('align', array($this, 'align')), + new Twig_SimpleFilter('indent', array($this, 'indent')), + ); + } + + /** + * @param string $current + * @param array $collection + * + * @return string + */ + public function align($current, $collection) + { + $elementLength = strlen($current); + $longestElement = $elementLength; + foreach ($collection as $element) { + if ('Memio\Model\Phpdoc\ParameterTag' === get_class($element)) { + $longestElement = max($longestElement, strlen($element->getType())); + } + } + + return $current.str_repeat(' ', $longestElement - $elementLength); + } + + /** + * @param string $text + * @param int $level + * @param string $type + * + * @return string + */ + public function indent($text, $level = 1, $type = 'code') + { + $lines = explode("\n", $text); + $indentedLines = array(); + if ('code' === $type) { + foreach ($lines as $line) { + $indentedLines[] = ' '.$line; + } + } + if ('phpdoc' === $type) { + foreach ($lines as $line) { + $indent = ' *'; + if (!empty($line)) { + $indent .= ' '; + } + $indentedLines[] = $indent.$line; + } + } + + return implode("\n", $indentedLines); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'whitespace'; + } +} diff --git a/src/Memio/TwigTemplateEngine/TwigTemplateEngine.php b/src/Memio/TwigTemplateEngine/TwigTemplateEngine.php new file mode 100644 index 0000000..80c0a6d --- /dev/null +++ b/src/Memio/TwigTemplateEngine/TwigTemplateEngine.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Memio\TwigTemplateEngine; + +use Memio\PrettyPrinter\TemplateEngine; +use Twig_Environment; + +class TwigTemplateEngine implements TemplateEngine +{ + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @param Twig_Environment $twig + */ + public function __construct(Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritDoc} + */ + public function addPath($path) + { + $this->twig->getLoader()->addPath($path); + } + + /** + * {@inheritDoc} + */ + public function render($template, array $parameters = array()) + { + return $this->twig->render($template.'.twig', $parameters); + } +} diff --git a/templates/argument.twig b/templates/argument.twig new file mode 100644 index 0000000..5ac5abe --- /dev/null +++ b/templates/argument.twig @@ -0,0 +1,4 @@ +{{ has_typehint(argument) ? argument.type ~ ' ' }}${{ argument.name -}} +{% if argument.defaultValue%} + = {{ argument.defaultValue|raw -}} +{% endif %} diff --git a/templates/collection/argument_collection.twig b/templates/collection/argument_collection.twig new file mode 100644 index 0000000..202477e --- /dev/null +++ b/templates/collection/argument_collection.twig @@ -0,0 +1,8 @@ +{% set inline %} + {%- include 'collection/arguments/inline.twig' with { 'argument_collection': argument_collection } only -%} +{% endset %} +{% if inline|length > 120 - (length_restriction | default(0)) %} + {%- include 'collection/arguments/multiline.twig' with { 'argument_collection': argument_collection } only -%} +{% else %} + {{- inline -}} +{% endif %} diff --git a/templates/collection/arguments/inline.twig b/templates/collection/arguments/inline.twig new file mode 100644 index 0000000..ba49862 --- /dev/null +++ b/templates/collection/arguments/inline.twig @@ -0,0 +1,4 @@ +{% for argument in argument_collection %} + {%- include 'argument.twig' with { 'argument': argument } only -%} + {% if not loop.last %}, {% endif %} +{% endfor %} diff --git a/templates/collection/arguments/multiline.twig b/templates/collection/arguments/multiline.twig new file mode 100644 index 0000000..9151ede --- /dev/null +++ b/templates/collection/arguments/multiline.twig @@ -0,0 +1,5 @@ +{% for argument in argument_collection %} + + {% include 'argument.twig' with { 'argument': argument } only %}{{ (not loop.last) ? ',' }} +{%- endfor %} +{{ "\n " -}} diff --git a/templates/collection/constant_collection.twig b/templates/collection/constant_collection.twig new file mode 100644 index 0000000..1184187 --- /dev/null +++ b/templates/collection/constant_collection.twig @@ -0,0 +1,4 @@ +{% for constant in constant_collection -%} + {% include 'constant.twig' with { 'constant': constant } only %} + +{% endfor -%} diff --git a/templates/collection/contract_collection.twig b/templates/collection/contract_collection.twig new file mode 100644 index 0000000..cf41926 --- /dev/null +++ b/templates/collection/contract_collection.twig @@ -0,0 +1,4 @@ +{% for contract in contract_collection -%} + {{ contract.name }}{{ not loop.last ? ', ' }} +{%- endfor %} + diff --git a/templates/collection/fully_qualified_name_collection.twig b/templates/collection/fully_qualified_name_collection.twig new file mode 100644 index 0000000..4002fee --- /dev/null +++ b/templates/collection/fully_qualified_name_collection.twig @@ -0,0 +1,4 @@ +{% for fully_qualified_name in fully_qualified_name_collection %} +{% include 'fully_qualified_name.twig' with { 'fully_qualified_name': fully_qualified_name } only %} + +{% endfor -%} diff --git a/templates/collection/method_collection.twig b/templates/collection/method_collection.twig new file mode 100644 index 0000000..ec70718 --- /dev/null +++ b/templates/collection/method_collection.twig @@ -0,0 +1,3 @@ +{% for method in method_collection -%} + {% include 'method.twig' with { 'method': method } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/collection/methods/pure_virtual.twig b/templates/collection/methods/pure_virtual.twig new file mode 100644 index 0000000..424c727 --- /dev/null +++ b/templates/collection/methods/pure_virtual.twig @@ -0,0 +1,7 @@ +{% if method.phpdoc %}{% include 'phpdoc/method_phpdoc.twig' with {'method_phpdoc': method.phpdoc} %}{% endif %} + {{ method.visibility ~ (method.visibility is not empty ? ' ') -}} + function {{ method.name }}({% include 'collection/argument_collection.twig' with { + 'argument_collection': method.allArguments, + 'length_restriction': 22 + method.name | length + } only %}); +{#- Trimming lines -#} diff --git a/templates/collection/methods/pure_virtual_collection.twig b/templates/collection/methods/pure_virtual_collection.twig new file mode 100644 index 0000000..c91dad2 --- /dev/null +++ b/templates/collection/methods/pure_virtual_collection.twig @@ -0,0 +1,3 @@ +{% for method in method_collection -%} + {% include 'collection/methods/pure_virtual.twig' with { 'method': method } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/collection/phpdoc/parameter_tag_collection.twig b/templates/collection/phpdoc/parameter_tag_collection.twig new file mode 100644 index 0000000..c83e3e1 --- /dev/null +++ b/templates/collection/phpdoc/parameter_tag_collection.twig @@ -0,0 +1,7 @@ +{% for parameter_tag in parameter_tag_collection -%} + {% include 'phpdoc/parameter_tag.twig' with { + 'parameter_tag': parameter_tag, + 'parameter_tag_collection': parameter_tag_collection + } only -%} + {{ not loop.last ? "\n" }} +{%- endfor -%} diff --git a/templates/collection/property_collection.twig b/templates/collection/property_collection.twig new file mode 100644 index 0000000..1bc205f --- /dev/null +++ b/templates/collection/property_collection.twig @@ -0,0 +1,3 @@ +{% for property in property_collection -%} + {% include 'property.twig' with { 'property': property } only %}{{ loop.last ? "\n" : "\n\n" }} +{%- endfor -%} diff --git a/templates/constant.twig b/templates/constant.twig new file mode 100644 index 0000000..b5f8df4 --- /dev/null +++ b/templates/constant.twig @@ -0,0 +1,2 @@ + const {{ constant.name }} = {{ constant.value|raw }}; +{#- Trimming lines -#} diff --git a/templates/contract.twig b/templates/contract.twig new file mode 100644 index 0000000..cfdd0a1 --- /dev/null +++ b/templates/contract.twig @@ -0,0 +1,15 @@ +{% if contract.phpdoc %}{% include 'phpdoc/structure_phpdoc.twig' with {'structure_phpdoc': contract.phpdoc} %}{% endif %} +interface {{ contract.name }} + {{- contract.allContracts is not empty ? ' extends ' }} + {%- include 'collection/contract_collection.twig' with { 'contract_collection': contract.allContracts } only %} +{ +{% include 'collection/constant_collection.twig' with { + 'constant_collection': contract.allConstants +} only %} +{% if needs_line_after(contract, 'constants') %} + +{% endif %} +{% include 'collection/methods/pure_virtual_collection.twig' with { + 'method_collection': contract.allMethods +} only %} +} diff --git a/templates/file.twig b/templates/file.twig new file mode 100644 index 0000000..d943336 --- /dev/null +++ b/templates/file.twig @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ diff --git a/templates/phpdoc/method_phpdoc.twig b/templates/phpdoc/method_phpdoc.twig new file mode 100644 index 0000000..4763ada --- /dev/null +++ b/templates/phpdoc/method_phpdoc.twig @@ -0,0 +1,21 @@ +{% set phpdoc_content %} +{% if method_phpdoc.description %}{% include 'phpdoc/description.twig' with { 'description': method_phpdoc.description } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'description') ? "\n\n" -}} +{% if method_phpdoc.parameterTags %}{% include 'collection/phpdoc/parameter_tag_collection.twig' with { 'parameter_tag_collection': method_phpdoc.parameterTags } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'parameter_tags') ? "\n\n" -}} +{% if method_phpdoc.deprecationTag %}{% include 'phpdoc/deprecation_tag.twig' with { 'deprecation_tag': method_phpdoc.deprecationTag } only %}{% endif %} +{{- needs_line_after(method_phpdoc, 'deprecation_tag') ? "\n\n" -}} +{% if method_phpdoc.apiTag %}{% include 'phpdoc/api_tag.twig' with { 'api_tag': method_phpdoc.apiTag } only %}{% endif %} +{% endset -%} + +{% set phpdoc %} +{% if not method_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{%- endif %} +{% endset -%} + +{% if not method_phpdoc.isEmpty %} +{{ phpdoc|indent(1) }} +{% endif %} diff --git a/templates/phpdoc/parameter_tag.twig b/templates/phpdoc/parameter_tag.twig new file mode 100644 index 0000000..efb0b4c --- /dev/null +++ b/templates/phpdoc/parameter_tag.twig @@ -0,0 +1,3 @@ +{% set with_collection = parameter_tag_collection is not defined ? [] : parameter_tag_collection %} +@param {{ parameter_tag.type|align(with_collection) }} ${{ parameter_tag.name }}{{ parameter_tag.description ? ' ' ~ parameter_tag.description }} +{#- Trimming lines -#} diff --git a/templates/phpdoc/property_phpdoc.twig b/templates/phpdoc/property_phpdoc.twig new file mode 100644 index 0000000..e179ee6 --- /dev/null +++ b/templates/phpdoc/property_phpdoc.twig @@ -0,0 +1,15 @@ +{% set phpdoc_content %} +{% if property_phpdoc.VariableTag %}{% include 'phpdoc/variable_tag.twig' with { 'variable_tag': property_phpdoc.VariableTag } only %}{% endif %} +{% endset -%} + +{% set phpdoc %} +{% if not property_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{%- endif %} +{% endset -%} + +{% if not property_phpdoc.isEmpty %} +{{ phpdoc|indent(1) }} +{% endif %} diff --git a/templates/phpdoc/structure_phpdoc.twig b/templates/phpdoc/structure_phpdoc.twig new file mode 100644 index 0000000..910ea67 --- /dev/null +++ b/templates/phpdoc/structure_phpdoc.twig @@ -0,0 +1,12 @@ +{% set phpdoc_content %} +{% if structure_phpdoc.description %}{% include 'phpdoc/description.twig' with { 'description': structure_phpdoc.description } only %}{% endif %} +{{- needs_line_after(structure_phpdoc, 'description') ? "\n\n" -}} +{% if structure_phpdoc.deprecationTag %}{% include 'phpdoc/deprecation_tag.twig' with { 'deprecation_tag': structure_phpdoc.deprecationTag } only %}{% endif %} +{{- needs_line_after(structure_phpdoc, 'deprecation_tag') ? "\n\n" -}} +{% if structure_phpdoc.apiTag %}{% include 'phpdoc/api_tag.twig' with { 'api_tag': structure_phpdoc.apiTag } only %}{% endif %} +{% endset %} +{% if not structure_phpdoc.isEmpty %} +/** +{{ phpdoc_content|indent(1, 'phpdoc') }} + */ +{% endif %} diff --git a/templates/phpdoc/variable_tag.twig b/templates/phpdoc/variable_tag.twig new file mode 100644 index 0000000..b49f69f --- /dev/null +++ b/templates/phpdoc/variable_tag.twig @@ -0,0 +1,2 @@ +@var {{ variable_tag.type }} +{#- Trimming lines -#} diff --git a/templates/property.twig b/templates/property.twig new file mode 100644 index 0000000..8f5c9f5 --- /dev/null +++ b/templates/property.twig @@ -0,0 +1,7 @@ +{% if property.phpdoc %}{% include 'phpdoc/property_phpdoc.twig' with {'property_phpdoc': property.phpdoc} %}{% endif %} +{#- Trimming lines #} + {{ property.visibility ~ ' ' }} + {{- property.isStatic ? 'static ' -}} + ${{ property.name -}} + {{ property.defaultValue ? (' = ' ~ property.defaultValue)|raw }}; +{#- Trimming lines -#}