From 9a10b3bfc7712d57464f570761a28b8a25a80b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 10:40:59 +0200 Subject: [PATCH 1/8] Add ability to define declare statements This adds the ability to configure declare() statements at the top of generated files. It is only possible to generate valid declare statements as stated in php documentation https://www.php.net/manual/en/control-structures.declare.php Solves #102 --- src/Declare_.php | 115 ++++++++++++++++++++++++ src/Generator/FileGenerator.php | 53 +++++++++++ test/Generator/FileGeneratorTest.php | 128 +++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 src/Declare_.php diff --git a/src/Declare_.php b/src/Declare_.php new file mode 100644 index 00000000..cddf2fc8 --- /dev/null +++ b/src/Declare_.php @@ -0,0 +1,115 @@ + 'integer', + self::STRICT_TYPES => 'integer', + self::ENCODING => 'string' + ]; + + /** + * @var string + */ + protected $directive; + + /** + * @var int|string + */ + protected $value; + + private function __construct(string $directive, $value) + { + $this->directive = $directive; + $this->value = $value; + } + + /** + * @return string + */ + public function getDirective(): string + { + return $this->directive; + } + + /** + * @return int|string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param int $value + * @return Declare_ + */ + public static function ticks(int $value): Declare_ + { + return new self(self::TICKS, $value); + } + + /** + * @param int $value + * @return Declare_ + */ + public static function strictTypes(int $value): Declare_ + { + return new self(self::STRICT_TYPES, $value); + } + + /** + * @param string $value + * @return Declare_ + */ + public static function encoding(string $value): Declare_ + { + return new self(self::ENCODING, $value); + } + + public static function fromArray(array $config): Declare_ + { + $directive = array_key_first($config); + $value = $config[$directive]; + + if (! array_key_exists($directive, self::ALLOWED)) { + throw new InvalidArgumentException( + sprintf( + 'Declare directive must be on of: %s.', + implode(', ', array_keys(self::ALLOWED)) + ) + ); + } + + if (gettype($value) !== self::ALLOWED[$directive]) { + throw new InvalidArgumentException( + sprintf( + 'Declare value invalid. Expected %s got %s.', + self::ALLOWED[$directive], + gettype($value) + ) + ); + } + + $method = str_replace('_', '', lcfirst(ucwords($directive, '_'))); + return self::{$method}($value); + } + + /** + * @return string + */ + public function getStatement(): string + { + $value = is_string($this->value) ? '\'' . $this->value . '\'' : $this->value; + + return sprintf('declare(%s=%s);', $this->directive, $value); + } +} diff --git a/src/Generator/FileGenerator.php b/src/Generator/FileGenerator.php index 8253b603..ff2a0acf 100644 --- a/src/Generator/FileGenerator.php +++ b/src/Generator/FileGenerator.php @@ -9,6 +9,8 @@ namespace Zend\Code\Generator; +use Zend\Code\Declare_; +use Zend\Code\Exception\InvalidArgumentException; use Zend\Code\Reflection\Exception as ReflectionException; use Zend\Code\Reflection\FileReflection; @@ -72,6 +74,11 @@ class FileGenerator extends AbstractGenerator */ protected $body; + /** + * @var Declare_[] + */ + protected $declares = []; + /** * Passes $options to {@link setOptions()}. * @@ -166,6 +173,11 @@ public static function fromArray(array $values) case 'requiredfiles': $fileGenerator->setRequiredFiles($value); break; + case 'declares': + $fileGenerator->setDeclares(array_map(function ($directive, $value) { + return Declare_::fromArray([$directive => $value]); + }, array_keys($value), $value)); + break; default: if (property_exists($fileGenerator, $name)) { $fileGenerator->{$name} = $value; @@ -408,6 +420,25 @@ public function getBody() return $this->body; } + public function setDeclares(array $declares) + { + foreach ($declares as $declare) { + if (! $declare instanceof Declare_) { + throw new InvalidArgumentException(sprintf( + '%s is expecting an array of %s\Declare objects', + __METHOD__, + __NAMESPACE__ + )); + } + + if (! array_key_exists($declare->getDirective(), $this->declares)) { + $this->declares[$declare->getDirective()] = $declare; + } + } + + return $this; + } + /** * @return bool */ @@ -491,6 +522,28 @@ public function generate() } } + // declares, if any + if ($this->declares) { + $declareStatements = ''; + + foreach ($this->declares as $declare) { + $declareStatements .= $declare->getStatement() . self::LINE_FEED; + } + + if (preg_match('#/\* Zend_Code_Generator_FileGenerator-DeclaresMarker \*/#m', $output)) { + $output = preg_replace( + '#/\* Zend_Code_Generator_FileGenerator-DeclaresMarker \*/#m', + $declareStatements, + $output, + 1 + ); + } else { + $output .= $declareStatements; + } + + $output .= self::LINE_FEED; + } + // process required files // @todo marker replacement for required files $requiredFiles = $this->getRequiredFiles(); diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index f2ec0bc9..6b97ca0b 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -10,6 +10,8 @@ namespace ZendTest\Code\Generator; use PHPUnit\Framework\TestCase; +use Zend\Code\Declare_; +use Zend\Code\Exception\InvalidArgumentException; use Zend\Code\Generator\ClassGenerator; use Zend\Code\Generator\FileGenerator; use Zend\Code\Reflection\FileReflection; @@ -413,4 +415,130 @@ public function added() $actual = file_get_contents(sys_get_temp_dir() . '/result_class.php'); self::assertEquals($expected, $actual); } + + public function testSingleDeclareStatement(): void + { + $generator = FileGenerator::fromArray([ + 'declares' => [ + 'strict_types' => 1 + ], + 'class' => [ + 'name' => 'SampleClass' + ], + ]); + $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); + $generator->write(); + + $expected = <<assertEquals($expected, $actual); + } + + public function testMultiDeclareStatements(): void + { + $generator = FileGenerator::fromArray([ + 'declares' => [ + 'strict_types' => 1, + 'ticks' => 2 + ], + 'class' => [ + 'name' => 'SampleClass' + ], + ]); + $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); + $generator->write(); + + $expected = <<assertEquals($expected, $actual); + } + + public function testDeclareUnknownDirectiveShouldRaiseException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Declare directive must be on of: tick, strict_types, encoding.'); + + FileGenerator::fromArray([ + 'declares' => [ + 'fubar' => 1 + ], + 'class' => [ + 'name' => 'SampleClass' + ], + ]); + } + + public function testDeclareWrongTypeShouldRaiseException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Declare value invalid. Expected integer got string.'); + + FileGenerator::fromArray([ + 'declares' => [ + 'strict_types' => 'wrong type' + ], + 'class' => [ + 'name' => 'SampleClass' + ], + ]); + } + + public function testDeclareDuplicatesShouldOnlyGenerateOne(): void + { + $generator = FileGenerator::fromArray([ + 'class' => [ + 'name' => 'SampleClass' + ], + ]); + $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); + $generator->setDeclares([ + Declare_::strictTypes(1), + Declare_::strictTypes(2) + ]); + $generator->write(); + + $expected = <<assertEquals($expected, $actual); + } } From 06bc7b7ea766e01d66c2341abf74a5bcc544147f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 10:47:13 +0200 Subject: [PATCH 2/8] Use php7.1 compliant functionality --- src/Declare_.php | 2 +- test/Generator/FileGeneratorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Declare_.php b/src/Declare_.php index cddf2fc8..89808b6b 100644 --- a/src/Declare_.php +++ b/src/Declare_.php @@ -77,7 +77,7 @@ public static function encoding(string $value): Declare_ public static function fromArray(array $config): Declare_ { - $directive = array_key_first($config); + $directive = key($config); $value = $config[$directive]; if (! array_key_exists($directive, self::ALLOWED)) { diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index 6b97ca0b..b6188a4d 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -483,7 +483,7 @@ class SampleClass public function testDeclareUnknownDirectiveShouldRaiseException(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Declare directive must be on of: tick, strict_types, encoding.'); + $this->expectExceptionMessage('Declare directive must be on of: ticks, strict_types, encoding.'); FileGenerator::fromArray([ 'declares' => [ From 1a82658f8914ab62a6c073a1ed32f163e1710a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 10:50:16 +0200 Subject: [PATCH 3/8] Rename class to match codesniffer rules --- src/{Declare_.php => DeclareStatement.php} | 16 ++++++++-------- src/Generator/FileGenerator.php | 8 ++++---- test/Generator/FileGeneratorTest.php | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) rename src/{Declare_.php => DeclareStatement.php} (85%) diff --git a/src/Declare_.php b/src/DeclareStatement.php similarity index 85% rename from src/Declare_.php rename to src/DeclareStatement.php index 89808b6b..073be7fb 100644 --- a/src/Declare_.php +++ b/src/DeclareStatement.php @@ -4,7 +4,7 @@ use Zend\Code\Exception\InvalidArgumentException; -class Declare_ +class DeclareStatement { public const TICKS = 'ticks'; public const STRICT_TYPES = 'strict_types'; @@ -50,32 +50,32 @@ public function getValue() /** * @param int $value - * @return Declare_ + * @return DeclareStatement */ - public static function ticks(int $value): Declare_ + public static function ticks(int $value): DeclareStatement { return new self(self::TICKS, $value); } /** * @param int $value - * @return Declare_ + * @return DeclareStatement */ - public static function strictTypes(int $value): Declare_ + public static function strictTypes(int $value): DeclareStatement { return new self(self::STRICT_TYPES, $value); } /** * @param string $value - * @return Declare_ + * @return DeclareStatement */ - public static function encoding(string $value): Declare_ + public static function encoding(string $value): DeclareStatement { return new self(self::ENCODING, $value); } - public static function fromArray(array $config): Declare_ + public static function fromArray(array $config): DeclareStatement { $directive = key($config); $value = $config[$directive]; diff --git a/src/Generator/FileGenerator.php b/src/Generator/FileGenerator.php index ff2a0acf..1cef3167 100644 --- a/src/Generator/FileGenerator.php +++ b/src/Generator/FileGenerator.php @@ -9,7 +9,7 @@ namespace Zend\Code\Generator; -use Zend\Code\Declare_; +use Zend\Code\DeclareStatement; use Zend\Code\Exception\InvalidArgumentException; use Zend\Code\Reflection\Exception as ReflectionException; use Zend\Code\Reflection\FileReflection; @@ -75,7 +75,7 @@ class FileGenerator extends AbstractGenerator protected $body; /** - * @var Declare_[] + * @var DeclareStatement[] */ protected $declares = []; @@ -175,7 +175,7 @@ public static function fromArray(array $values) break; case 'declares': $fileGenerator->setDeclares(array_map(function ($directive, $value) { - return Declare_::fromArray([$directive => $value]); + return DeclareStatement::fromArray([$directive => $value]); }, array_keys($value), $value)); break; default: @@ -423,7 +423,7 @@ public function getBody() public function setDeclares(array $declares) { foreach ($declares as $declare) { - if (! $declare instanceof Declare_) { + if (! $declare instanceof DeclareStatement) { throw new InvalidArgumentException(sprintf( '%s is expecting an array of %s\Declare objects', __METHOD__, diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index b6188a4d..17e14c6f 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -10,7 +10,7 @@ namespace ZendTest\Code\Generator; use PHPUnit\Framework\TestCase; -use Zend\Code\Declare_; +use Zend\Code\DeclareStatement; use Zend\Code\Exception\InvalidArgumentException; use Zend\Code\Generator\ClassGenerator; use Zend\Code\Generator\FileGenerator; @@ -519,8 +519,8 @@ public function testDeclareDuplicatesShouldOnlyGenerateOne(): void ]); $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); $generator->setDeclares([ - Declare_::strictTypes(1), - Declare_::strictTypes(2) + DeclareStatement::strictTypes(1), + DeclareStatement::strictTypes(2) ]); $generator->write(); From 423e4c6c6c7741ba4b016889a4acf6f21e187e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 11:27:10 +0200 Subject: [PATCH 4/8] Code linting --- src/DeclareStatement.php | 19 ++++++++++--------- src/Generator/FileGenerator.php | 6 +++--- test/Generator/FileGeneratorTest.php | 18 +++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/DeclareStatement.php b/src/DeclareStatement.php index 073be7fb..5bdb670a 100644 --- a/src/DeclareStatement.php +++ b/src/DeclareStatement.php @@ -13,7 +13,7 @@ class DeclareStatement private const ALLOWED = [ self::TICKS => 'integer', self::STRICT_TYPES => 'integer', - self::ENCODING => 'string' + self::ENCODING => 'string', ]; /** @@ -50,37 +50,37 @@ public function getValue() /** * @param int $value - * @return DeclareStatement + * @return self */ - public static function ticks(int $value): DeclareStatement + public static function ticks(int $value): self { return new self(self::TICKS, $value); } /** * @param int $value - * @return DeclareStatement + * @return self */ - public static function strictTypes(int $value): DeclareStatement + public static function strictTypes(int $value): self { return new self(self::STRICT_TYPES, $value); } /** * @param string $value - * @return DeclareStatement + * @return self */ - public static function encoding(string $value): DeclareStatement + public static function encoding(string $value): self { return new self(self::ENCODING, $value); } - public static function fromArray(array $config): DeclareStatement + public static function fromArray(array $config): self { $directive = key($config); $value = $config[$directive]; - if (! array_key_exists($directive, self::ALLOWED)) { + if (! isset(self::ALLOWED[$directive])) { throw new InvalidArgumentException( sprintf( 'Declare directive must be on of: %s.', @@ -100,6 +100,7 @@ public static function fromArray(array $config): DeclareStatement } $method = str_replace('_', '', lcfirst(ucwords($directive, '_'))); + return self::{$method}($value); } diff --git a/src/Generator/FileGenerator.php b/src/Generator/FileGenerator.php index 1cef3167..73af4cab 100644 --- a/src/Generator/FileGenerator.php +++ b/src/Generator/FileGenerator.php @@ -174,7 +174,7 @@ public static function fromArray(array $values) $fileGenerator->setRequiredFiles($value); break; case 'declares': - $fileGenerator->setDeclares(array_map(function ($directive, $value) { + $fileGenerator->setDeclares(array_map(static function ($directive, $value) { return DeclareStatement::fromArray([$directive => $value]); }, array_keys($value), $value)); break; @@ -425,9 +425,9 @@ public function setDeclares(array $declares) foreach ($declares as $declare) { if (! $declare instanceof DeclareStatement) { throw new InvalidArgumentException(sprintf( - '%s is expecting an array of %s\Declare objects', + '%s is expecting an array of %s objects', __METHOD__, - __NAMESPACE__ + DeclareStatement::class )); } diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index 17e14c6f..98c8f05e 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -420,10 +420,10 @@ public function testSingleDeclareStatement(): void { $generator = FileGenerator::fromArray([ 'declares' => [ - 'strict_types' => 1 + 'strict_types' => 1, ], 'class' => [ - 'name' => 'SampleClass' + 'name' => 'SampleClass', ], ]); $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); @@ -452,10 +452,10 @@ public function testMultiDeclareStatements(): void $generator = FileGenerator::fromArray([ 'declares' => [ 'strict_types' => 1, - 'ticks' => 2 + 'ticks' => 2, ], 'class' => [ - 'name' => 'SampleClass' + 'name' => 'SampleClass', ], ]); $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); @@ -487,10 +487,10 @@ public function testDeclareUnknownDirectiveShouldRaiseException(): void FileGenerator::fromArray([ 'declares' => [ - 'fubar' => 1 + 'fubar' => 1, ], 'class' => [ - 'name' => 'SampleClass' + 'name' => 'SampleClass', ], ]); } @@ -502,10 +502,10 @@ public function testDeclareWrongTypeShouldRaiseException(): void FileGenerator::fromArray([ 'declares' => [ - 'strict_types' => 'wrong type' + 'strict_types' => 'wrong type', ], 'class' => [ - 'name' => 'SampleClass' + 'name' => 'SampleClass', ], ]); } @@ -514,7 +514,7 @@ public function testDeclareDuplicatesShouldOnlyGenerateOne(): void { $generator = FileGenerator::fromArray([ 'class' => [ - 'name' => 'SampleClass' + 'name' => 'SampleClass', ], ]); $generator->setFilename(sys_get_temp_dir() . '/result_file.php'); From bfd0c485ba84b334d61bcf27d25e1c96efd5220e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 11:31:30 +0200 Subject: [PATCH 5/8] Improve test coverage --- test/Generator/FileGeneratorTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index 98c8f05e..fab3fdc9 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -541,4 +541,13 @@ class SampleClass $actual = file_get_contents(sys_get_temp_dir() . '/result_file.php'); $this->assertEquals($expected, $actual); } + + public function testWrongDeclareTypeShouldRaiseException(): void + { + $generator = new FileGenerator(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('setDeclares is expecting an array of Zend\\Code\\DeclareStatement objects'); + $generator->setDeclares([new \stdClass()]); + } } From 3e25e05bd6fba9af659c6a611a4dc644e8ce2aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 17:32:32 +0200 Subject: [PATCH 6/8] Fix grammar and typo --- src/DeclareStatement.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeclareStatement.php b/src/DeclareStatement.php index 5bdb670a..1d8e21c2 100644 --- a/src/DeclareStatement.php +++ b/src/DeclareStatement.php @@ -83,7 +83,7 @@ public static function fromArray(array $config): self if (! isset(self::ALLOWED[$directive])) { throw new InvalidArgumentException( sprintf( - 'Declare directive must be on of: %s.', + 'Declare directive must be one of: %s.', implode(', ', array_keys(self::ALLOWED)) ) ); @@ -92,7 +92,7 @@ public static function fromArray(array $config): self if (gettype($value) !== self::ALLOWED[$directive]) { throw new InvalidArgumentException( sprintf( - 'Declare value invalid. Expected %s got %s.', + 'Declare value invalid. Expected %s, got %s.', self::ALLOWED[$directive], gettype($value) ) From 6918deee0ab6e36ae1f550489404e8ee9f8b6147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Fr=C3=B6mer?= Date: Mon, 17 Jun 2019 17:54:47 +0200 Subject: [PATCH 7/8] Fix tests after typo --- test/Generator/FileGeneratorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Generator/FileGeneratorTest.php b/test/Generator/FileGeneratorTest.php index fab3fdc9..c75747e2 100644 --- a/test/Generator/FileGeneratorTest.php +++ b/test/Generator/FileGeneratorTest.php @@ -483,7 +483,7 @@ class SampleClass public function testDeclareUnknownDirectiveShouldRaiseException(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Declare directive must be on of: ticks, strict_types, encoding.'); + $this->expectExceptionMessage('Declare directive must be one of: ticks, strict_types, encoding.'); FileGenerator::fromArray([ 'declares' => [ @@ -498,7 +498,7 @@ public function testDeclareUnknownDirectiveShouldRaiseException(): void public function testDeclareWrongTypeShouldRaiseException(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Declare value invalid. Expected integer got string.'); + $this->expectExceptionMessage('Declare value invalid. Expected integer, got string.'); FileGenerator::fromArray([ 'declares' => [ From 3743c263c8f57bacb4494441f133b330c050579b Mon Sep 17 00:00:00 2001 From: webimpress Date: Sat, 5 Oct 2019 23:38:45 +0100 Subject: [PATCH 8/8] Adds CHANGELOG entry for #169 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b6ab9b..cf69a8b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file, in reverse - [#170](https://github.com/zendframework/zend-code/pull/170) adds class constant visibility modifiers support. +- [#169](https://github.com/zendframework/zend-code/pull/169) adds the ability to define declare statements. + - [#167](https://github.com/zendframework/zend-code/pull/167) adds the ability to remove doc block of a member. ### Changed