├── .coveralls.yml ├── .gitignore ├── .php_cs ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── bin └── prooph-cli ├── build ├── .gitignore └── logs │ └── .gitignore ├── composer.json ├── phpunit.xml.dist ├── src ├── Autoloader.php ├── Code │ └── Generator │ │ ├── AbstractGenerator.php │ │ ├── AddEventToAggregate.php │ │ ├── Aggregate.php │ │ ├── Command.php │ │ ├── CommandHandler.php │ │ ├── CommandHandlerFactory.php │ │ ├── Event.php │ │ ├── Generator.php │ │ ├── ReflectionGenerator.php │ │ └── ReplaceNamespaceTrait.php ├── Console │ ├── Command │ │ ├── AbstractGenerateCommand.php │ │ ├── GenerateAggregate.php │ │ ├── GenerateAll.php │ │ ├── GenerateCommand.php │ │ └── GenerateEvent.php │ ├── Container │ │ ├── GenerateAggregateFactory.php │ │ ├── GenerateCommandFactory.php │ │ └── GenerateEventFactory.php │ └── Helper │ │ ├── AbstractClassInfo.php │ │ ├── ClassInfo.php │ │ └── Psr4Info.php └── Exception │ ├── CliException.php │ ├── FileExistsException.php │ ├── InvalidArgumentException.php │ └── RuntimeException.php └── tests └── Console └── Helper └── Psr4InfoTest.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | # for php-coveralls 2 | service_name: travis-ci 3 | src_dir: src 4 | coverage_clover: build/logs/clover.xml 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in('src') 4 | ->in('tests'); 5 | $config = Symfony\CS\Config\Config::create(); 6 | $config->level(null); 7 | $config->fixers( 8 | array( 9 | 'braces', 10 | 'duplicate_semicolon', 11 | 'elseif', 12 | 'empty_return', 13 | 'encoding', 14 | 'eof_ending', 15 | 'function_call_space', 16 | 'function_declaration', 17 | 'indentation', 18 | 'join_function', 19 | 'line_after_namespace', 20 | 'linefeed', 21 | 'lowercase_keywords', 22 | 'parenthesis', 23 | 'multiple_use', 24 | 'method_argument_space', 25 | 'object_operator', 26 | 'php_closing_tag', 27 | 'remove_lines_between_uses', 28 | 'short_array_syntax', 29 | 'short_tag', 30 | 'standardize_not_equal', 31 | 'trailing_spaces', 32 | 'unused_use', 33 | 'visibility', 34 | 'whitespacy_lines', 35 | ) 36 | ); 37 | $config->finder($finder); 38 | return $config; 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | cache: 6 | directories: 7 | - $HOME/.composer/cache 8 | - vendor 9 | 10 | matrix: 11 | fast_finish: true 12 | include: 13 | - php: 5.5 14 | env: 15 | - EXECUTE_CS_CHECK=true 16 | - php: 5.6 17 | env: 18 | - EXECUTE_TEST_COVERALLS=true 19 | - php: 7 20 | - php: hhvm 21 | allow_failures: 22 | - php: hhvm 23 | 24 | before_install: 25 | - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi 26 | - composer self-update 27 | - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer require --dev --no-update satooshi/php-coveralls:dev-master ; fi 28 | 29 | install: 30 | - travis_retry composer install --no-interaction 31 | - composer info -i 32 | 33 | script: 34 | - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer test-coverage ; fi 35 | - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then composer test ; fi 36 | - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then composer cs ; fi 37 | 38 | after_script: 39 | - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer coveralls ; fi 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # prooph Command Line Interface CHANGELOG 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 0.2.0 (2016-04-28) 6 | 7 | ### Added 8 | 9 | * [#14](https://github.com/proophsoftware/prooph-cli/pull/4): ability to specify environment variable options as cli options 10 | * [#4](https://github.com/proophsoftware/prooph-cli/pull/4): use class info instance from container 11 | 12 | ### Deprecated 13 | 14 | * Nothing 15 | 16 | ### Removed 17 | 18 | * [#11](https://github.com/proophsoftware/prooph-cli/pull/11): Zend Framework Service Manager and unnecessary dependencies 19 | 20 | ### Fixed 21 | 22 | * [#18](https://github.com/proophsoftware/prooph-cli/pull/18): double generation of aggregate 23 | 24 | ## 0.1.2 (2016-01-22) 25 | 26 | ### Added 27 | 28 | * Nothing 29 | 30 | ### Deprecated 31 | 32 | * Nothing 33 | 34 | ### Removed 35 | 36 | * Nothing 37 | 38 | ### Fixed 39 | 40 | * Fixed wrong path and namespace handling depending on user input 41 | 42 | ## 0.1.1 (2016-01-20) 43 | 44 | ### Added 45 | 46 | * Nothing 47 | 48 | ### Deprecated 49 | 50 | * Nothing 51 | 52 | ### Removed 53 | 54 | * Nothing 55 | 56 | ### Fixed 57 | 58 | * Fixed include paths 59 | * Fixed directory separator for class path 60 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, prooph software GmbH 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | - Neither the name of prooph software GmbH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prooph Command Line Interface 2 | 3 | ## Overview 4 | The prooph command line interface generates classes to increase development speed. For available commands run 5 | 6 | ```bash 7 | $ php bin/prooph-cli list 8 | ``` 9 | 10 | Here is an example output: 11 | ```shell 12 | Available commands: 13 | help Displays help for a command 14 | list Lists commands 15 | prooph 16 | prooph:generate:aggregate Generates an aggregate class 17 | prooph:generate:all Generates an aggregate, command, command handler, command handler factory and event class. 18 | prooph:generate:command Generates a command, command handler and command handler factory class 19 | prooph:generate:event Generates an event class 20 | ``` 21 | 22 | There are available environment variables (see `bin/prooph-cli`) for bash scripts to configure 23 | `\Prooph\Cli\Console\Helper\Psr4Info` for your class meta data. 24 | 25 | ## Installation 26 | 27 | You can install prooph/prooph-cli via composer by adding `"proophsoftware/prooph-cli": "^0.1"` as requirement to your composer.json. 28 | 29 | ## Configuration 30 | This tool checks if a [container-interop](https://github.com/container-interop/container-interop) instance is returned 31 | from file `config/container.php`. You can configure the class info metadata by registering an instance with name 32 | `\Prooph\Cli\Console\Helper\ClassInfo` like `\Prooph\Cli\Console\Helper\Psr4Info` to the container. 33 | 34 | Another option is to use environment variables to configure your class metadata: 35 | 36 | **env variables:** 37 | 38 | * `PROOPHCLI_SOURCE_FOLDER`: path to src folder, default current working dir + 'src' 39 | * `PROOPHCLI_PACKAGE_PREFIX`: namespace of package, default '' 40 | * `PROOPHCLI_FILE_DOC_BLOCk`: file doc block, default '' 41 | 42 | ## Support 43 | 44 | - Ask questions on [prooph-users](https://groups.google.com/forum/?hl=de#!forum/prooph) mailing list. 45 | - File issues at [https://github.com/proophsoftware/prooph-cli/issues](https://github.com/proophsoftware/prooph-cli/issues). 46 | - Say hello in the [prooph gitter](https://gitter.im/prooph/improoph) chat. 47 | 48 | ## Contribute 49 | 50 | Please feel free to fork and extend existing or add new plugins and send a pull request with your changes! 51 | To establish a consistent code quality, please provide unit tests for all your changes and may adapt the documentation. 52 | 53 | ## License 54 | 55 | Released under the [New BSD License](LICENSE). 56 | -------------------------------------------------------------------------------- /bin/prooph-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | ')) { 12 | fwrite( 13 | STDERR, 14 | 'This version of prooph-cli requires PHP 5.5; using the latest version of PHP is highly recommended.' . PHP_EOL 15 | ); 16 | 17 | die(1); 18 | } 19 | 20 | if (!ini_get('date.timezone')) { 21 | ini_set('date.timezone', 'UTC'); 22 | } 23 | 24 | foreach ( 25 | [ 26 | __DIR__ . '/../../../autoload.php', 27 | __DIR__ . '/../../autoload.php', 28 | __DIR__ . '/../vendor/autoload.php', 29 | __DIR__ . '/vendor/autoload.php', 30 | ] as $file 31 | ) { 32 | if (file_exists($file)) { 33 | define('PROOPHCLI_COMPOSER_INSTALL', $file); 34 | 35 | break; 36 | } 37 | } 38 | 39 | unset($file); 40 | 41 | if (!defined('PROOPHCLI_COMPOSER_INSTALL')) { 42 | fwrite(STDERR, 43 | 'You need to set up the project dependencies using the following commands:' . PHP_EOL . 44 | 'wget http://getcomposer.org/composer.phar' . PHP_EOL . 45 | 'php composer.phar install' . PHP_EOL 46 | ); 47 | 48 | die(1); 49 | } 50 | 51 | require PROOPHCLI_COMPOSER_INSTALL; 52 | 53 | use Symfony\Component\Console\Application; 54 | use Prooph\Cli\Console\Command; 55 | use Prooph\Cli\Code\Generator; 56 | 57 | $containerFile = 'config' . DIRECTORY_SEPARATOR . 'container.php'; 58 | 59 | $container = null; 60 | 61 | if (file_exists($containerFile)) { 62 | $container = require $containerFile; 63 | } 64 | 65 | $classInfo = null; 66 | 67 | if ($container instanceof \Interop\Container\ContainerInterface 68 | && $container->has(\Prooph\Cli\Console\Helper\ClassInfo::class) 69 | ) { 70 | $classInfo = $container->get(\Prooph\Cli\Console\Helper\ClassInfo::class); 71 | 72 | if (!$classInfo instanceof \Prooph\Cli\Console\Helper\ClassInfo) { 73 | throw new \Prooph\Cli\Exception\RuntimeException( 74 | 'Wrong instance registered. Class info must be an instance of \Prooph\Cli\Console\Helper\ClassInfo' 75 | ); 76 | } 77 | } 78 | 79 | if (null === $classInfo) { 80 | $classInfo = new \Prooph\Cli\Console\Helper\Psr4Info( 81 | getenv('PROOPHCLI_SOURCE_FOLDER') ?: getcwd() . DIRECTORY_SEPARATOR . 'src', 82 | getenv('PROOPHCLI_PACKAGE_PREFIX') ?: '', 83 | getenv('PROOPHCLI_FILE_DOC_BLOCk') ?: '' 84 | ); 85 | } 86 | 87 | $description = <<getHelperSet(); 108 | 109 | $helperSet->set($classInfo); 110 | 111 | spl_autoload_register(new \Prooph\Cli\Autoloader($classInfo)); 112 | 113 | $aggregate = new Generator\Aggregate(); 114 | $command = new Generator\Command(); 115 | $commandHandler = new Generator\CommandHandler(); 116 | $commandHandlerFactory = new Generator\CommandHandlerFactory(); 117 | $event = new Generator\Event(); 118 | 119 | $application->addCommands( 120 | [ 121 | new Command\GenerateAll(), 122 | new Command\GenerateCommand($command, $commandHandler, $commandHandlerFactory), 123 | new Command\GenerateAll(), 124 | new Command\GenerateEvent($event), 125 | new Command\GenerateAggregate($aggregate) 126 | ] 127 | ); 128 | 129 | $application->run(); 130 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | * 3 | -------------------------------------------------------------------------------- /build/logs/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | * 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proophsoftware/prooph-cli", 3 | "description": "prooph components command line tool for rapid development", 4 | "type": "library", 5 | "license": "BSD-3-Clause", 6 | "homepage": "http://prooph-software.com/", 7 | "authors": [ 8 | { 9 | "name": "Alexander Miertsch", 10 | "email": "contact@prooph.de", 11 | "homepage": "http://prooph-software.com/" 12 | }, 13 | { 14 | "name": "Sandro Keil", 15 | "email": "contact@prooph.de", 16 | "homepage": "http://prooph-software.com/" 17 | } 18 | ], 19 | "keywords": [ 20 | "cli", 21 | "tool", 22 | "rapid", 23 | "development", 24 | "prooph", 25 | "components", 26 | "code", 27 | "generation" 28 | ], 29 | "require": { 30 | "php": "~5.5 || ~7.0", 31 | "symfony/console": "^2.5 || ^3.0", 32 | "zendframework/zend-code": "^2.6.2 || ^3.0.1", 33 | "zendframework/zend-filter": "^2.6.1 || ^3.0", 34 | "container-interop/container-interop": "^1.1" 35 | }, 36 | "require-dev": { 37 | "phpunit/phpunit": "^4.8 || ^5.2", 38 | "fabpot/php-cs-fixer": "^1.11", 39 | "prooph/event-sourcing": "^4.0" 40 | }, 41 | "suggest" : { 42 | "prooph/event-sourcing": "If you want to update a prooph event sourcing aggregate" 43 | }, 44 | "autoload": { 45 | "psr-4": { 46 | "Prooph\\Cli\\": "src/" 47 | } 48 | }, 49 | "autoload-dev": { 50 | "psr-4": { 51 | "ProophTest\\Cli\\": "tests/" 52 | } 53 | }, 54 | "bin": [ 55 | "bin/prooph-cli" 56 | ], 57 | "scripts": { 58 | "check": [ 59 | "@cs", 60 | "@test" 61 | ], 62 | "coveralls": "coveralls", 63 | "cs": "php-cs-fixer fix -v --diff --dry-run", 64 | "cs-fix": "php-cs-fixer fix -v --diff", 65 | "test": "phpunit", 66 | "test-coverage": "phpunit --coverage-clover clover.xml" 67 | }, 68 | "extra": { 69 | "branch-alias": { 70 | "dev-master": "1.0-dev", 71 | "dev-develop": "1.1-dev" 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | ./tests 16 | 17 | 18 | 19 | 20 | ./src/ 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Autoloader.php: -------------------------------------------------------------------------------- 1 | classInfo = $classInfo; 33 | } 34 | 35 | /** 36 | * Loads class depending on class info 37 | * 38 | * @param string $class 39 | * @return bool 40 | */ 41 | public function __invoke($class) 42 | { 43 | $file = $this->classInfo->getFilename( 44 | $this->classInfo->getPath($class), 45 | $this->classInfo->getClassName($class) 46 | ); 47 | 48 | if (file_exists($file)) { 49 | require $file; 50 | return true; 51 | } 52 | return false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Code/Generator/AbstractGenerator.php: -------------------------------------------------------------------------------- 1 | getImplementedInterfaces(); 27 | $properties = $this->getClassProperties(); 28 | $flags = $this->getClassFlags(); 29 | $methods = $this->getMethods($name, $namespace); 30 | $traits = $this->getTraits(); 31 | // getUses() must called at the end to allow other methods to add import namespace directives 32 | $uses = $this->getUses(); 33 | 34 | $fileDocBlock = new DocBlockGenerator($fileDocBlock); 35 | 36 | if ($classToExtend) { 37 | $uses[] = ltrim($classToExtend, '\\'); 38 | $classToExtend = substr($classToExtend, strrpos($classToExtend, '\\') + 1); 39 | } 40 | 41 | switch (get_class($this)) { 42 | case 'Prooph\Cli\Code\Generator\CommandHandler': 43 | $className = $name . 'Handler'; 44 | break; 45 | case 'Prooph\Cli\Code\Generator\CommandHandlerFactory': 46 | $className = $name . 'HandlerFactory'; 47 | break; 48 | default: 49 | $className = $name; 50 | break; 51 | } 52 | 53 | $class = new ClassGenerator( 54 | $className, 55 | $namespace, 56 | $flags, 57 | $classToExtend, 58 | $interfaces, 59 | $properties, 60 | $methods, 61 | $this->getClassDocBlock($name) 62 | ); 63 | 64 | foreach ($traits as $trait) { 65 | $class->addTrait($trait); 66 | } 67 | 68 | return new FileGenerator([ 69 | 'classes' => [$class], 70 | 'namespace' => $namespace, 71 | 'uses' => $uses, 72 | 'docBlock' => $fileDocBlock, 73 | ]); 74 | } 75 | 76 | /** 77 | * @interitdoc 78 | */ 79 | public function writeClass($filename, FileGenerator $fileGenerator) 80 | { 81 | $dir = dirname($filename); 82 | 83 | if (!@mkdir($dir, 0755, true) && !is_dir($dir)) { 84 | throw new RuntimeException(sprintf('Could not create "%s" directory.', $dir)); 85 | } 86 | 87 | file_put_contents($filename, $this->replaceNamespace($fileGenerator)); 88 | } 89 | 90 | /** 91 | * @param string $name Class name 92 | * @return DocBlockGenerator 93 | */ 94 | abstract protected function getClassDocBlock($name); 95 | 96 | /** 97 | * @return array List of namespace imports 98 | */ 99 | protected function getUses() 100 | { 101 | return []; 102 | } 103 | 104 | /** 105 | * @param string $name 106 | * @param string $namespace 107 | * @return array List of class methods 108 | */ 109 | protected function getMethods($name, $namespace) 110 | { 111 | return []; 112 | } 113 | 114 | /** 115 | * @return array List of implemented interfaces 116 | */ 117 | protected function getImplementedInterfaces() 118 | { 119 | return []; 120 | } 121 | 122 | /** 123 | * @return array List of class properties 124 | */ 125 | protected function getClassProperties() 126 | { 127 | return []; 128 | } 129 | 130 | /** 131 | * @return null|array List of class flags 132 | */ 133 | protected function getClassFlags() 134 | { 135 | return [ClassGenerator::FLAG_FINAL]; 136 | } 137 | 138 | /** 139 | * @return null|array List of used Traits 140 | */ 141 | protected function getTraits() 142 | { 143 | return []; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Code/Generator/AddEventToAggregate.php: -------------------------------------------------------------------------------- 1 | setFilename($file); 38 | 39 | $classGenerator = $fileGenerator->getClass(); 40 | 41 | /* @var $method MethodGenerator */ 42 | foreach ($classGenerator->getMethods() as $method) { 43 | if (strpos($method->getName(), 'when') !== 0) { 44 | continue; 45 | } 46 | /* @var $parameter ParameterGenerator */ 47 | foreach ($method->getParameters() as $parameter) { 48 | $fileGenerator->setUse($parameter->getType()); 49 | } 50 | } 51 | 52 | $namespace = ltrim($namespace, '\\') . '\\'; 53 | $fileGenerator->setUse($namespace . $commandName); 54 | 55 | 56 | // workaround for import namespace 57 | if ($classToExtend = $classGenerator->getExtendedClass()) { 58 | $classGenerator->setExtendedClass(substr($classToExtend, strrpos($classToExtend, '\\') + 1)); 59 | } 60 | 61 | $classGenerator->addMethodFromGenerator($this->methodWhenEvent($commandName, $namespace)); 62 | 63 | return $fileGenerator; 64 | } 65 | 66 | /** 67 | * @inheritDoc 68 | */ 69 | public function writeClass(FileGenerator $fileGenerator) 70 | { 71 | file_put_contents($fileGenerator->getFilename(), $this->replaceNamespace($fileGenerator)); 72 | } 73 | 74 | /** 75 | * Build when[Event]() method 76 | * 77 | * @param string $name 78 | * @return MethodGenerator 79 | */ 80 | private function methodWhenEvent($name, $namespace) 81 | { 82 | $parameters = [ 83 | new ParameterGenerator( 84 | 'event', '\\' . $namespace . $name 85 | ), 86 | ]; 87 | 88 | return new MethodGenerator( 89 | 'when' . $name, 90 | $parameters, 91 | MethodGenerator::FLAG_PROTECTED, 92 | '', 93 | new DocBlockGenerator( 94 | 'Updates aggregate if event ' . $name . ' was occurred', 95 | null, 96 | [ 97 | new ParamTag('event', [ '\\' . $namespace . $name]), 98 | ] 99 | ) 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Code/Generator/Aggregate.php: -------------------------------------------------------------------------------- 1 | methodAggregateId(), 36 | ]; 37 | } 38 | 39 | /** 40 | * Build __invoke method 41 | * 42 | * @return MethodGenerator 43 | */ 44 | private function methodAggregateId() 45 | { 46 | return new MethodGenerator( 47 | 'aggregateId', 48 | [], 49 | MethodGenerator::FLAG_PROTECTED, 50 | 'return ;', 51 | new DocBlockGenerator( 52 | null, 53 | null, 54 | [ 55 | new GenericTag('inheritDoc'), 56 | ] 57 | ) 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Code/Generator/Command.php: -------------------------------------------------------------------------------- 1 | uses; 32 | } 33 | 34 | /** 35 | * @interitdoc 36 | */ 37 | protected function getClassDocBlock($name) 38 | { 39 | return new DocBlockGenerator('Command ' . $name); 40 | } 41 | 42 | /** 43 | * @inheritDoc 44 | */ 45 | protected function getImplementedInterfaces() 46 | { 47 | $this->uses[] = 'Prooph\Common\Messaging\PayloadConstructable'; 48 | return ['PayloadConstructable']; 49 | } 50 | 51 | /** 52 | * @inheritDoc 53 | */ 54 | protected function getTraits() 55 | { 56 | $this->uses[] = 'Prooph\Common\Messaging\PayloadTrait'; 57 | return ['PayloadTrait']; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Code/Generator/CommandHandler.php: -------------------------------------------------------------------------------- 1 | uses; 43 | } 44 | 45 | /** 46 | * @inheritDoc 47 | */ 48 | protected function getMethods($name, $namespace) 49 | { 50 | return [ 51 | $this->methodInvoke($name, $namespace), 52 | ]; 53 | } 54 | 55 | /** 56 | * Build __invoke method 57 | * 58 | * @param string $name 59 | * @param $namespace 60 | * @return MethodGenerator 61 | */ 62 | private function methodInvoke($name, $namespace) 63 | { 64 | $name = ucfirst($name); 65 | 66 | $this->uses[] = $namespace . '\\' . $name; 67 | 68 | $namespace = '\\' . $namespace . '\\'; 69 | 70 | $parameters = [ 71 | new ParameterGenerator( 72 | 'command', $namespace . $name 73 | ), 74 | ]; 75 | 76 | return new MethodGenerator( 77 | '__invoke', 78 | $parameters, 79 | MethodGenerator::FLAG_PUBLIC, 80 | '', 81 | new DocBlockGenerator( 82 | 'Handle command', 83 | null, 84 | [ 85 | new ParamTag('command', [ $namespace . $name]), 86 | ] 87 | ) 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Code/Generator/CommandHandlerFactory.php: -------------------------------------------------------------------------------- 1 | uses; 44 | } 45 | 46 | /** 47 | * @inheritDoc 48 | */ 49 | protected function getMethods($name, $namespace) 50 | { 51 | return [ 52 | $this->methodInvoke($name, $namespace), 53 | ]; 54 | } 55 | 56 | /** 57 | * Build __invoke method 58 | * 59 | * @param string $name 60 | * @param string $namespace 61 | * @return MethodGenerator 62 | */ 63 | private function methodInvoke($name, $namespace) 64 | { 65 | $name = ucfirst($name); 66 | 67 | $parameters = [ 68 | new ParameterGenerator('container', '\Interop\Container\ContainerInterface'), 69 | ]; 70 | $this->uses[] = 'Interop\Container\ContainerInterface'; 71 | 72 | return new MethodGenerator( 73 | '__invoke', 74 | $parameters, 75 | MethodGenerator::FLAG_PUBLIC, 76 | sprintf('return new %sHandler();', $name), 77 | new DocBlockGenerator( 78 | 'Creates command handler for command ' . $name, 79 | null, 80 | [ 81 | new ParamTag( 82 | 'container', 83 | [ 84 | '\Interop\Container\ContainerInterface', 85 | ] 86 | ), 87 | new ReturnTag([$name . 'Handler']), 88 | ] 89 | ) 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Code/Generator/Event.php: -------------------------------------------------------------------------------- 1 | getUses() as $import) { 32 | $namespace = trim(substr($import[0], 0, strrpos($import[0], '\\')), '\\'); 33 | 34 | if ($fileGenerator->getNamespace() !== $namespace) { 35 | $uses[] = $import; 36 | } 37 | 38 | $name = trim(substr($import[0], strrpos($import[0], '\\')), '\\'); 39 | $search[] = '\\' . $import[0]; 40 | $search[] = ', \\' . $import[0]; 41 | $replace[] = $name; 42 | $replace[] = ', ' . $name; 43 | } 44 | // workaround to reset use imports 45 | $reflection = new \ReflectionClass($fileGenerator); 46 | $property = $reflection->getProperty('uses'); 47 | $property->setAccessible(true); 48 | $property->setValue($fileGenerator, $uses); 49 | 50 | $code = $fileGenerator->generate(); 51 | return str_replace($search, $replace, $code); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Console/Command/AbstractGenerateCommand.php: -------------------------------------------------------------------------------- 1 | getArgument('name'); 27 | $path = $input->getArgument('path'); 28 | $classToExtend = $input->getArgument('class-to-extend'); 29 | 30 | /* @var $classInfo ClassInfo */ 31 | $classInfo = $this->getHelper(ClassInfo::class); 32 | 33 | if ($input->getOption('source-folder')) { 34 | $classInfo->setSourceFolder($input->getOption('source-folder')); 35 | } 36 | if ($input->getOption('package-prefix')) { 37 | $classInfo->setPackagePrefix($input->getOption('package-prefix')); 38 | } 39 | if ($input->getOption('file-doc-block')) { 40 | $classInfo->setFileDocBlock($input->getOption('file-doc-block')); 41 | } 42 | 43 | switch (get_class($generator)) { 44 | case 'Prooph\Cli\Code\Generator\CommandHandler': 45 | $className = $name . 'Handler'; 46 | break; 47 | case 'Prooph\Cli\Code\Generator\CommandHandlerFactory': 48 | $className = $name . 'HandlerFactory'; 49 | break; 50 | default: 51 | $className = $name; 52 | break; 53 | } 54 | 55 | $filename = $classInfo->getFilename($path, $className); 56 | 57 | if (file_exists($filename) && !$input->getOption('force')) { 58 | throw (new FileExistsException( 59 | sprintf('File "%s" already exists, use --force option to overwrite this file.', $filename) 60 | ))->withType(static::class); 61 | } 62 | /* @var $fileGenerator FileGenerator */ 63 | $fileGenerator = $generator( 64 | $name, $classInfo->getClassNamespace($path), $classToExtend, $classInfo->getFileDocBlock() 65 | ); 66 | 67 | if ($input->getOption('not-final')) { 68 | $fileGenerator->getClass()->removeFlag(ClassGenerator::FLAG_FINAL); 69 | } 70 | 71 | $generator->writeClass($filename, $fileGenerator); 72 | $output->writeln('Generated file ' . $filename, ''); 73 | } 74 | 75 | /** 76 | * @param string $fqcn 77 | * @param InputInterface $input 78 | * @param OutputInterface $output 79 | * @param ReflectionGenerator $generator 80 | */ 81 | protected function updateExistingClass( 82 | $fqcn, InputInterface $input, OutputInterface $output, ReflectionGenerator $generator 83 | ) { 84 | /* @var $classInfo ClassInfo */ 85 | $classInfo = $this->getHelper(ClassInfo::class); 86 | 87 | $file = $classInfo->getFilename( 88 | $classInfo->getPath($fqcn), 89 | $classInfo->getClassName($fqcn) 90 | ); 91 | 92 | // don't break code generation, this is only a benefit 93 | if (!file_exists($file)) { 94 | $output->writeln( 95 | sprintf('Event method was not added to the aggregate. File "%s" not found.', $file) 96 | ); 97 | return; 98 | } 99 | 100 | $path = $input->getArgument('path'); 101 | 102 | /* @var $fileGenerator FileGenerator */ 103 | $fileGenerator = $generator($file, $classInfo->getClassNamespace($path), $input->getArgument('name')); 104 | 105 | $generator->writeClass($fileGenerator); 106 | $output->writeln('Updated file ' . $fileGenerator->getFilename() . ''); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Console/Command/GenerateAggregate.php: -------------------------------------------------------------------------------- 1 | generator = $generator; 32 | 33 | parent::__construct(); 34 | } 35 | 36 | /** 37 | * @interitdoc 38 | */ 39 | protected function execute(InputInterface $input, OutputInterface $output) 40 | { 41 | if (!$input->getOption('disable-type-prefix')) { 42 | $input->setArgument('path', $input->getArgument('path') . '/Aggregate'); 43 | } 44 | 45 | $this->generateClass($input, $output, $this->generator); 46 | } 47 | 48 | protected function configure() 49 | { 50 | $this 51 | ->setName('prooph:generate:aggregate') 52 | ->setDescription('Generates an aggregate class') 53 | ->addArgument( 54 | 'name', 55 | InputArgument::REQUIRED, 56 | 'What is the name of the aggregate class?' 57 | ) 58 | ->addArgument( 59 | 'path', 60 | InputArgument::OPTIONAL, 61 | 'Path to store the file. Starts from configured source folder path.' 62 | ) 63 | ->addArgument( 64 | 'class-to-extend', 65 | InputArgument::OPTIONAL, 66 | 'FQCN of the base class , optional', 67 | '\Prooph\EventSourcing\AggregateRoot' 68 | ) 69 | ->addOption( 70 | 'force', 71 | 'f', 72 | InputOption::VALUE_NONE, 73 | 'Overwrite file if exists, optional' 74 | ) 75 | ->addOption( 76 | 'not-final', 77 | null, 78 | InputOption::VALUE_NONE, 79 | 'Mark class as NOT final, optional' 80 | ) 81 | ->addOption( 82 | 'disable-type-prefix', 83 | null, 84 | InputOption::VALUE_NONE, 85 | 'Use this flag if you not want to put the classes under the "Aggregate" namespace, optional' 86 | ) 87 | ->addOption( 88 | 'source-folder', 89 | null, 90 | InputOption::VALUE_OPTIONAL, 91 | 'Absolute path to the source folder.' 92 | ) 93 | ->addOption( 94 | 'package-prefix', 95 | null, 96 | InputOption::VALUE_OPTIONAL, 97 | 'Package prefix which is used as class namespace.' 98 | ) 99 | ->addOption( 100 | 'file-doc-block', 101 | null, 102 | InputOption::VALUE_OPTIONAL, 103 | 'Common PHP file doc block.' 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Console/Command/GenerateAll.php: -------------------------------------------------------------------------------- 1 | setName('prooph:generate:all') 30 | ->setDescription( 31 | 'Generates an aggregate, command, command handler, command handler factory and event class.' 32 | ) 33 | ->addArgument( 34 | 'command-name', 35 | InputArgument::REQUIRED, 36 | 'What is the name of the command class?' 37 | ) 38 | ->addArgument( 39 | 'event-name', 40 | InputArgument::REQUIRED, 41 | 'What is the name of the event class?' 42 | ) 43 | ->addArgument( 44 | 'aggregate-name', 45 | InputArgument::REQUIRED, 46 | 'What is the name of the aggregate class?' 47 | ) 48 | ->addArgument( 49 | 'path', 50 | InputArgument::OPTIONAL, 51 | 'Path to store the files. Starts from configured source folder path.' 52 | ) 53 | ->addOption( 54 | 'force', 55 | 'f', 56 | InputOption::VALUE_NONE, 57 | 'Overwrite all files if exists, optional' 58 | ) 59 | ->addOption( 60 | 'not-final', 61 | null, 62 | InputOption::VALUE_NONE, 63 | 'Mark class as NOT final, optional' 64 | ) 65 | ->addOption( 66 | 'disable-type-prefix', 67 | null, 68 | InputOption::VALUE_NONE, 69 | 'Use this flag if you not want to put the classes under the specific type namespace, optional' 70 | ) 71 | ->addOption( 72 | 'source-folder', 73 | null, 74 | InputOption::VALUE_OPTIONAL, 75 | 'Absolute path to the source folder.' 76 | ) 77 | ->addOption( 78 | 'package-prefix', 79 | null, 80 | InputOption::VALUE_OPTIONAL, 81 | 'Package prefix which is used as class namespace.' 82 | ) 83 | ->addOption( 84 | 'file-doc-block', 85 | null, 86 | InputOption::VALUE_OPTIONAL, 87 | 'Common PHP file doc block.' 88 | ); 89 | } 90 | 91 | /** 92 | * @interitdoc 93 | */ 94 | protected function execute(InputInterface $input, OutputInterface $output) 95 | { 96 | $commands = [ 97 | 'aggregate' => $input->getArgument('aggregate-name'), 98 | 'command' => $input->getArgument('command-name'), 99 | 'event' => $input->getArgument('event-name'), 100 | ]; 101 | 102 | $path = $input->getArgument('path'); 103 | 104 | foreach ($commands as $commandName => $name) { 105 | $command = $this->getApplication()->find('prooph:generate:' . $commandName); 106 | 107 | $arguments = [ 108 | 'name' => $name, 109 | 'path' => $path, 110 | '--force' => $input->getOption('force'), 111 | '--not-final' => $input->getOption('not-final'), 112 | '--disable-type-prefix' => $input->getOption('disable-type-prefix'), 113 | ]; 114 | 115 | $aggregatePath = rtrim($path, '/'); 116 | 117 | if (in_array($commandName, ['aggregate', 'event']) && !$input->getOption('disable-type-prefix')) { 118 | $aggregatePath .= DIRECTORY_SEPARATOR . 'Aggregate'; 119 | } 120 | 121 | /* @var $classInfo ClassInfo */ 122 | $classInfo = $this->getHelper(ClassInfo::class); 123 | 124 | if ($commandName === 'event') { 125 | $arguments['--update-aggregate'] = $classInfo->getClassNamespace($aggregatePath) . '\\' 126 | . $commands['aggregate']; 127 | } 128 | 129 | if ($input->getOption('source-folder')) { 130 | $classInfo->setSourceFolder($input->getOption('source-folder')); 131 | } 132 | if ($input->getOption('package-prefix')) { 133 | $classInfo->setPackagePrefix($input->getOption('package-prefix')); 134 | } 135 | if ($input->getOption('file-doc-block')) { 136 | $classInfo->setFileDocBlock($input->getOption('file-doc-block')); 137 | } 138 | 139 | try { 140 | $command->run(new ArrayInput($arguments), $output); 141 | } catch (FileExistsException $e) { 142 | // aggregate class will be updated with event 143 | if ($e->getType() !== GenerateAggregate::class) { 144 | throw $e; 145 | } 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Console/Command/GenerateCommand.php: -------------------------------------------------------------------------------- 1 | commandGenerator = $generator; 49 | $this->commandHandlerGenerator = $handlerGenerator; 50 | $this->commandHandlerFactoryGenerator = $factoryGenerator; 51 | 52 | parent::__construct(); 53 | } 54 | 55 | /** 56 | * @interitdoc 57 | */ 58 | protected function configure() 59 | { 60 | $this 61 | ->setName('prooph:generate:command') 62 | ->setDescription('Generates a command, command handler and command handler factory class') 63 | ->addArgument( 64 | 'name', 65 | InputArgument::REQUIRED, 66 | 'What is the name of the command class?' 67 | ) 68 | ->addArgument( 69 | 'path', 70 | InputArgument::OPTIONAL, 71 | 'Path to store the file. Starts from configured source folder path.' 72 | ) 73 | ->addArgument( 74 | 'class-to-extend', 75 | InputArgument::OPTIONAL, 76 | 'FQCN of the base class , optional', 77 | '\Prooph\Common\Messaging\Command' 78 | ) 79 | ->addOption( 80 | 'force', 81 | 'f', 82 | InputOption::VALUE_NONE, 83 | 'Overwrite file if exists, optional' 84 | ) 85 | ->addOption( 86 | 'not-final', 87 | null, 88 | InputOption::VALUE_NONE, 89 | 'Mark class as NOT final, optional' 90 | ) 91 | ->addOption( 92 | 'disable-type-prefix', 93 | null, 94 | InputOption::VALUE_NONE, 95 | 'Use this flag if you not want to put the classes under the "Command" namespace, optional' 96 | ) 97 | ->addOption( 98 | 'source-folder', 99 | null, 100 | InputOption::VALUE_OPTIONAL, 101 | 'Absolute path to the source folder.' 102 | ) 103 | ->addOption( 104 | 'package-prefix', 105 | null, 106 | InputOption::VALUE_OPTIONAL, 107 | 'Package prefix which is used as class namespace.' 108 | ) 109 | ->addOption( 110 | 'file-doc-block', 111 | null, 112 | InputOption::VALUE_OPTIONAL, 113 | 'Common PHP file doc block.' 114 | ); 115 | } 116 | 117 | /** 118 | * @interitdoc 119 | */ 120 | protected function execute(InputInterface $input, OutputInterface $output) 121 | { 122 | if (!$input->getOption('disable-type-prefix')) { 123 | $input->setArgument('path', $input->getArgument('path') . '/Command'); 124 | } 125 | 126 | $this->generateClass($input, $output, $this->commandGenerator); 127 | 128 | $input->setArgument('class-to-extend', ''); 129 | 130 | $this->generateClass($input, $output, $this->commandHandlerGenerator); 131 | $this->generateClass($input, $output, $this->commandHandlerFactoryGenerator); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Console/Command/GenerateEvent.php: -------------------------------------------------------------------------------- 1 | generator = $generator; 36 | 37 | parent::__construct(); 38 | } 39 | 40 | /** 41 | * @interitdoc 42 | */ 43 | protected function execute(InputInterface $input, OutputInterface $output) 44 | { 45 | if (!$input->getOption('disable-type-prefix')) { 46 | $input->setArgument('path', $input->getArgument('path') . '/Event'); 47 | } 48 | 49 | $this->generateClass($input, $output, $this->generator); 50 | 51 | if ($aggregateClass = $input->getOption('update-aggregate')) { 52 | $this->createAggregateClass($aggregateClass, $input, $output); 53 | 54 | $this->updateExistingClass($aggregateClass, $input, $output, new AddEventToAggregate()); 55 | } 56 | } 57 | 58 | /** 59 | * @param string $aggregateClass FQCN 60 | * @param OutputInterface $output 61 | */ 62 | protected function createAggregateClass($aggregateClass, InputInterface $input, OutputInterface $output) 63 | { 64 | $filename = ''; 65 | 66 | try { 67 | $command = $this->getApplication()->find('prooph:generate:aggregate'); 68 | 69 | /* @var $classInfo ClassInfo */ 70 | $classInfo = $this->getHelper(ClassInfo::class); 71 | 72 | $arguments = [ 73 | 'name' => $classInfo->getClassName($aggregateClass), 74 | 'path' => $classInfo->getPath($aggregateClass), 75 | '--disable-type-prefix' => true, 76 | ]; 77 | 78 | $filename = $classInfo->getFilename($arguments['path'], $arguments['name']); 79 | 80 | $command->run(new ArrayInput($arguments), $output); 81 | } catch (FileExistsException $e) { 82 | $output->writeln(sprintf('Skip generation of aggregate class "%s". File already exists.', $filename)); 83 | } 84 | } 85 | 86 | /** 87 | * @interitdoc 88 | */ 89 | protected function configure() 90 | { 91 | $this 92 | ->setName('prooph:generate:event') 93 | ->setDescription('Generates an event class') 94 | ->addArgument( 95 | 'name', 96 | InputArgument::REQUIRED, 97 | 'What is the name of the event?' 98 | ) 99 | ->addArgument( 100 | 'path', 101 | InputArgument::OPTIONAL, 102 | 'Path to store the file. Starts from configured source folder path.' 103 | ) 104 | ->addArgument( 105 | 'class-to-extend', 106 | InputArgument::OPTIONAL, 107 | 'FQCN of the base class , optional', 108 | '\Prooph\EventSourcing\AggregateChanged' 109 | ) 110 | ->addOption( 111 | 'force', 112 | 'f', 113 | InputOption::VALUE_NONE, 114 | 'Overwrite file if exists, optional' 115 | ) 116 | ->addOption( 117 | 'not-final', 118 | null, 119 | InputOption::VALUE_NONE, 120 | 'Mark class as NOT final, optional' 121 | ) 122 | ->addOption( 123 | 'update-aggregate', 124 | null, 125 | InputOption::VALUE_OPTIONAL, 126 | 'FQCN of an aggregate to add event method, optional' 127 | ) 128 | ->addOption( 129 | 'disable-type-prefix', 130 | null, 131 | InputOption::VALUE_NONE, 132 | 'Use this flag if you not want to put the classes under the "Event" namespace, optional' 133 | ) 134 | ->addOption( 135 | 'source-folder', 136 | null, 137 | InputOption::VALUE_OPTIONAL, 138 | 'Absolute path to the source folder.' 139 | ) 140 | ->addOption( 141 | 'package-prefix', 142 | null, 143 | InputOption::VALUE_OPTIONAL, 144 | 'Package prefix which is used as class namespace.' 145 | ) 146 | ->addOption( 147 | 'file-doc-block', 148 | null, 149 | InputOption::VALUE_OPTIONAL, 150 | 'Common PHP file doc block.' 151 | ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Console/Container/GenerateAggregateFactory.php: -------------------------------------------------------------------------------- 1 | get(AggregateGenerator::class) 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Container/GenerateCommandFactory.php: -------------------------------------------------------------------------------- 1 | get(Command::class), 38 | $container->get(CommandHandler::class), 39 | $container->get(CommandHandlerFactory::class) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Console/Container/GenerateEventFactory.php: -------------------------------------------------------------------------------- 1 | get(EventGenerator::class) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Console/Helper/AbstractClassInfo.php: -------------------------------------------------------------------------------- 1 | helperSet = $helperSet; 32 | } 33 | 34 | /** 35 | * Gets the helper set associated with this helper. 36 | * 37 | * @return HelperSet A HelperSet instance 38 | */ 39 | public function getHelperSet() 40 | { 41 | return $this->helperSet; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Console/Helper/ClassInfo.php: -------------------------------------------------------------------------------- 1 | sourceFolder = rtrim($sourceFolder, '/'); 62 | $this->packagePrefix = trim($packagePrefix, '\\'); 63 | $this->fileDocBlock = $fileDocBlock; 64 | } 65 | 66 | /** 67 | * @inheritDoc 68 | */ 69 | public function getName() 70 | { 71 | return ClassInfo::class; 72 | } 73 | 74 | /** 75 | * @inheritDoc 76 | */ 77 | public function getPackagePrefix() 78 | { 79 | return $this->packagePrefix; 80 | } 81 | 82 | /** 83 | * @inheritDoc 84 | */ 85 | public function setPackagePrefix($packagePrefix) 86 | { 87 | $this->packagePrefix = $packagePrefix; 88 | } 89 | 90 | /** 91 | * @inheritDoc 92 | */ 93 | public function getClassNamespace($path) 94 | { 95 | $namespace = $this->filterDirectoryToNamespace()->filter($path); 96 | 97 | return $this->normalizeNamespace($this->getPackagePrefix() . '\\' . $namespace); 98 | } 99 | 100 | /** 101 | * @inheritDoc 102 | */ 103 | public function getClassName($fqcn) 104 | { 105 | $fqcn = $this->normalizeNamespace($fqcn); 106 | return trim(substr($fqcn, strrpos($fqcn, '\\')), '\\'); 107 | } 108 | 109 | /** 110 | * @inheritDoc 111 | */ 112 | public function getPath($fqcn) 113 | { 114 | $fqcn = $this->normalizeNamespace($fqcn); 115 | $namespace = str_replace($this->getPackagePrefix(), '', $fqcn); 116 | $namespace = ltrim(substr($namespace, 0, strrpos($namespace, '\\')), '\\'); 117 | return $this->filterNamespaceToDirectory()->filter($namespace); 118 | } 119 | 120 | /** 121 | * @inheritDoc 122 | */ 123 | public function getFilename($path, $name) 124 | { 125 | $filePath = $this->getSourceFolder() . DIRECTORY_SEPARATOR; 126 | 127 | if ($path = trim($path, '/')) { 128 | $filePath .= $this->normalizePath($path) . DIRECTORY_SEPARATOR; 129 | } 130 | 131 | return $filePath . ucfirst($name) . '.php'; 132 | } 133 | 134 | /** 135 | * @inheritDoc 136 | */ 137 | public function getSourceFolder() 138 | { 139 | return $this->sourceFolder; 140 | } 141 | 142 | /** 143 | * @inheritDoc 144 | */ 145 | public function setSourceFolder($sourceFolder) 146 | { 147 | $this->sourceFolder = $sourceFolder; 148 | } 149 | 150 | /** 151 | * @inheritDoc 152 | */ 153 | public function getFileDocBlock() 154 | { 155 | return $this->fileDocBlock; 156 | } 157 | 158 | /** 159 | * @inheritDoc 160 | */ 161 | public function setFileDocBlock($fileDocBlock) 162 | { 163 | $this->fileDocBlock = $fileDocBlock; 164 | } 165 | 166 | /** 167 | * Removes duplicates of backslashes and trims backslashes 168 | * 169 | * @param string $namespace 170 | * @return string 171 | */ 172 | private function normalizeNamespace($namespace) 173 | { 174 | $namespace = str_replace('\\\\', '\\', $namespace); 175 | $namespace = explode('\\', $namespace); 176 | 177 | array_walk($namespace, function (&$item) { $item = ucfirst($item); }); 178 | 179 | return trim(implode('\\', $namespace), '\\'); 180 | } 181 | 182 | /** 183 | * PSR-4 folders must be upper camel case 184 | * 185 | * @param string $path 186 | * @return string 187 | */ 188 | private function normalizePath($path) 189 | { 190 | $path = explode('/', $path); 191 | 192 | array_walk($path, function (&$item) { $item = ucfirst($item); }); 193 | 194 | return implode('/', $path); 195 | } 196 | 197 | /** 198 | * @return FilterChain 199 | */ 200 | private function filterDirectoryToNamespace() 201 | { 202 | if (null === $this->filterDirectoryToNamespace) { 203 | $this->filterDirectoryToNamespace = new FilterChain(); 204 | $this->filterDirectoryToNamespace->attach(new SeparatorToSeparator(DIRECTORY_SEPARATOR, '|')); 205 | $this->filterDirectoryToNamespace->attach(new SeparatorToSeparator('|', '\\\\')); 206 | } 207 | return $this->filterDirectoryToNamespace; 208 | } 209 | 210 | /** 211 | * @return FilterChain 212 | */ 213 | private function filterNamespaceToDirectory() 214 | { 215 | if (null === $this->filterNamespaceToDirectory) { 216 | $this->filterNamespaceToDirectory = new FilterChain(); 217 | $this->filterNamespaceToDirectory->attach(new SeparatorToSeparator('\\', '|')); 218 | $this->filterNamespaceToDirectory->attach(new SeparatorToSeparator('|', DIRECTORY_SEPARATOR)); 219 | } 220 | return $this->filterNamespaceToDirectory; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Exception/CliException.php: -------------------------------------------------------------------------------- 1 | type; 27 | } 28 | 29 | /** 30 | * @param string $type 31 | * @return $this 32 | */ 33 | public function withType($type) 34 | { 35 | $this->type = $type; 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | getClassNamespace($path)); 30 | } 31 | 32 | /** 33 | * Values are expected, sourceFolder, packagePrefix and path 34 | * 35 | * @return array 36 | */ 37 | public function providerForGetClassNamespace() 38 | { 39 | return [ 40 | [ 41 | 'MyVendor\MyPackage\ModelPath\UserPath', 42 | 'src', 43 | '\MyVendor\MyPackage\\', 44 | 'ModelPath/UserPath', 45 | ], 46 | [ 47 | 'MyVendor\MyPackage\ModelPath\UserPath', 48 | 'src', 49 | '\\MyVendor\\MyPackage\\', 50 | '/ModelPath/UserPath/', 51 | ], 52 | [ 53 | 'Vendor\Package\Model\User', 54 | 'src', 55 | 'vendor\package', 56 | 'model/user/', 57 | ], 58 | [ 59 | 'Vendor\Package', 60 | 'src', 61 | 'vendor\package', 62 | '', 63 | ], 64 | [ 65 | 'Vendor', 66 | 'src', 67 | 'vendor', 68 | '', 69 | ], 70 | [ 71 | '', 72 | 'src', 73 | '', 74 | '', 75 | ], 76 | ]; 77 | } 78 | 79 | /** 80 | * @test 81 | * @dataProvider providerForGetPath 82 | * @covers Prooph\Cli\Console\Helper\Psr4Info::__construct 83 | * @covers Prooph\Cli\Console\Helper\Psr4Info::getPath 84 | * @covers Prooph\Cli\Console\Helper\Psr4Info::filterNamespaceToDirectory 85 | * @covers Prooph\Cli\Console\Helper\Psr4Info::normalizeNamespace 86 | */ 87 | public function it_returns_path_from_namespace($expected, $sourceFolder, $packagePrefix, $fcqn) 88 | { 89 | $psr4Info = new Psr4Info($sourceFolder, $packagePrefix); 90 | 91 | self::assertSame($expected, $psr4Info->getPath($fcqn)); 92 | } 93 | 94 | /** 95 | * Values are expected, sourceFolder, packagePrefix and fcqn 96 | * 97 | * @return array 98 | */ 99 | public function providerForGetPath() 100 | { 101 | return [ 102 | [ 103 | 'ModelPath/UserPath', 104 | 'src', 105 | '\MyVendor\MyPackage\\', 106 | '\MyVendor\MyPackage\ModelPath\UserPath\User', 107 | ], 108 | [ 109 | 'ModelPath/UserPath', 110 | 'src', 111 | '\\MyVendor\\MyPackage\\', 112 | '\\MyVendor\\MyPackage\\ModelPath\\UserPath\\User', 113 | ], 114 | [ 115 | '', 116 | 'src', 117 | 'MyVendor\MyPackage', 118 | 'MyVendor\MyPackage\User', 119 | ], 120 | ]; 121 | } 122 | 123 | /** 124 | * @test 125 | * @dataProvider providerForGetFilename 126 | * @covers Prooph\Cli\Console\Helper\Psr4Info::__construct 127 | * @covers Prooph\Cli\Console\Helper\Psr4Info::getFilename 128 | * @covers Prooph\Cli\Console\Helper\Psr4Info::normalizePath 129 | */ 130 | public function it_returns_filename($expected, $sourceFolder, $packagePrefix, $path, $name) 131 | { 132 | $psr4Info = new Psr4Info($sourceFolder, $packagePrefix); 133 | 134 | self::assertSame($expected, $psr4Info->getFilename($path, $name)); 135 | } 136 | 137 | /** 138 | * Values are expected, sourceFolder, packagePrefix, path and name 139 | * 140 | * @return array 141 | */ 142 | public function providerForGetFilename() 143 | { 144 | return [ 145 | [ 146 | 'src/ModelPath/UserPath/User.php', 147 | 'src', 148 | '\MyVendor\MyPackage\\', 149 | 'ModelPath/UserPath', 150 | 'User' 151 | ], 152 | [ 153 | 'src/ModelPath/UserPath/User.php', 154 | 'src', 155 | '\\MyVendor\\MyPackage\\', 156 | 'ModelPath/UserPath/', 157 | 'User' 158 | ], 159 | [ 160 | 'src/Model/User.php', 161 | 'src', 162 | 'vendor\package', 163 | '/model/', 164 | 'user' 165 | ], 166 | [ 167 | '/src/User.php', 168 | '/src/', 169 | 'vendor\package', 170 | '', 171 | 'user' 172 | ], 173 | ]; 174 | } 175 | 176 | /** 177 | * @test 178 | * @dataProvider providerForGetClassName 179 | * @covers Prooph\Cli\Console\Helper\Psr4Info::getClassName 180 | */ 181 | public function it_returns_class_name($expected, $sourceFolder, $packagePrefix, $fqcn) 182 | { 183 | $psr4Info = new Psr4Info($sourceFolder, $packagePrefix); 184 | 185 | self::assertSame($expected, $psr4Info->getClassName($fqcn)); 186 | } 187 | 188 | /** 189 | * Values are expected, sourceFolder, packagePrefix and FQCN 190 | * 191 | * @return array 192 | */ 193 | public function providerForGetClassName() 194 | { 195 | return [ 196 | [ 197 | 'User', 198 | 'src', 199 | '\MyVendor\MyPackage\\', 200 | '\MyVendor\MyPackage\ModelPath\UserPath\User', 201 | ], 202 | [ 203 | 'User', 204 | 'src', 205 | '\\MyVendor\\MyPackage\\', 206 | '\\MyVendor\\MyPackage\\ModelPath\\UserPath\\User', 207 | ], 208 | [ 209 | 'User', 210 | 'src', 211 | '', 212 | 'MyVendor\MyPackage\User', 213 | ], 214 | ]; 215 | } 216 | } 217 | --------------------------------------------------------------------------------