From 7eb1b355952c5bd4ed4e97934e0d838dba73c65b Mon Sep 17 00:00:00 2001 From: Moritz Bischof <59830009+19bischof@users.noreply.github.com> Date: Fri, 29 Sep 2023 12:20:31 +0200 Subject: [PATCH 1/5] Printer: fixed falsy Attribute aren't printed (#142) --- src/PhpGenerator/Printer.php | 2 +- tests/PhpGenerator/ClassType.attributes.phpt | 2 +- tests/PhpGenerator/expected/ClassType.attributes.expect | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpGenerator/Printer.php b/src/PhpGenerator/Printer.php index 83f45d9e..02888fa2 100644 --- a/src/PhpGenerator/Printer.php +++ b/src/PhpGenerator/Printer.php @@ -424,7 +424,7 @@ protected function printAttributes(array $attrs, bool $inline = false): string foreach ($attrs as $attr) { $args = $this->dumper->format('...?:', $attr->getArguments()); $args = Helpers::simplifyTaggedNames($args, $this->namespace); - $items[] = $this->printType($attr->getName(), nullable: false) . ($args ? "($args)" : ''); + $items[] = $this->printType($attr->getName(), nullable: false) . ($args === '' ? '' : "($args)"); $inline = $inline && !str_contains($args, "\n"); } diff --git a/tests/PhpGenerator/ClassType.attributes.phpt b/tests/PhpGenerator/ClassType.attributes.phpt index 8ec05719..c2dc1c76 100644 --- a/tests/PhpGenerator/ClassType.attributes.phpt +++ b/tests/PhpGenerator/ClassType.attributes.phpt @@ -41,6 +41,6 @@ $method = $class->addMethod('getHandle') $method->addParameter('mode') ->addComment('comment') ->addAttribute('ExampleAttribute') - ->addAttribute('WithArguments', [123]); + ->addAttribute('WithArguments', [0]); sameFile(__DIR__ . '/expected/ClassType.attributes.expect', (string) $class); diff --git a/tests/PhpGenerator/expected/ClassType.attributes.expect b/tests/PhpGenerator/expected/ClassType.attributes.expect index f9125620..434918ec 100644 --- a/tests/PhpGenerator/expected/ClassType.attributes.expect +++ b/tests/PhpGenerator/expected/ClassType.attributes.expect @@ -22,7 +22,7 @@ class Example #[ExampleAttribute] public function getHandle( /** comment */ - #[ExampleAttribute, WithArguments(123)] + #[ExampleAttribute, WithArguments(0)] $mode, ) { } From 250bf02df3d4a0f7992722ba922336fad7a53412 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 13 Oct 2023 21:47:36 +0200 Subject: [PATCH 2/5] Factory: $withBodies cannot be used for internal classes/functions --- src/PhpGenerator/Factory.php | 8 ++++---- tests/PhpGenerator/ClassType.from.bodies.phpt | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/PhpGenerator/Factory.php b/src/PhpGenerator/Factory.php index 61b24dca..589c4b64 100644 --- a/src/PhpGenerator/Factory.php +++ b/src/PhpGenerator/Factory.php @@ -37,8 +37,8 @@ public function fromClassReflection( if ($materializeTraits !== null) { trigger_error(__METHOD__ . '() parameter $materializeTraits has been removed (is always false).', E_USER_DEPRECATED); } - if ($withBodies && $from->isAnonymous()) { - throw new Nette\NotSupportedException('The $withBodies parameter cannot be used for anonymous functions.'); + if ($withBodies && ($from->isAnonymous() || $from->isInternal())) { + throw new Nette\NotSupportedException('The $withBodies parameter cannot be used for anonymous or internal classes.'); } $enumIface = null; @@ -183,8 +183,8 @@ public function fromFunctionReflection(\ReflectionFunction $from, bool $withBody $function->setReturnType((string) $from->getReturnType()); if ($withBody) { - if ($from->isClosure()) { - throw new Nette\NotSupportedException('The $withBody parameter cannot be used for closures.'); + if ($from->isClosure() || $from->isInternal()) { + throw new Nette\NotSupportedException('The $withBody parameter cannot be used for closures or internal functions.'); } $function->setBody($this->getExtractor($from)->extractFunctionBody($from->name)); diff --git a/tests/PhpGenerator/ClassType.from.bodies.phpt b/tests/PhpGenerator/ClassType.from.bodies.phpt index a98a66f0..d0c667e8 100644 --- a/tests/PhpGenerator/ClassType.from.bodies.phpt +++ b/tests/PhpGenerator/ClassType.from.bodies.phpt @@ -11,8 +11,8 @@ require __DIR__ . '/fixtures/bodies.php'; Assert::exception( fn() => ClassType::from(PDO::class, withBodies: true), - Nette\InvalidStateException::class, - 'Source code of PDO not found.', + Nette\NotSupportedException::class, + 'The $withBodies parameter cannot be used for anonymous or internal classes.', ); @@ -23,7 +23,7 @@ Assert::exception( } }, withBodies: true), Nette\NotSupportedException::class, - 'The $withBodies parameter cannot be used for anonymous functions.', + 'The $withBodies parameter cannot be used for anonymous or internal classes.', ); From 83968a12089210ae7eb68d4534ee487f3e93337a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 15 Oct 2023 01:36:52 +0200 Subject: [PATCH 3/5] Extractor::extractAll() fixed file comment parsing --- src/PhpGenerator/Extractor.php | 6 ++- .../Extractor.extractAll.file.comments.phpt | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/PhpGenerator/Extractor.extractAll.file.comments.phpt diff --git a/src/PhpGenerator/Extractor.php b/src/PhpGenerator/Extractor.php index 7ec3b3b7..6b00ad49 100644 --- a/src/PhpGenerator/Extractor.php +++ b/src/PhpGenerator/Extractor.php @@ -209,7 +209,11 @@ public function enterNode(Node $node) } }; - if ($this->statements) { + if ( + $this->statements + && !$this->statements[0] instanceof Node\Stmt\ClassLike + && !$this->statements[0] instanceof Node\Stmt\Function_ + ) { $this->addCommentAndAttributes($phpFile, $this->statements[0]); } diff --git a/tests/PhpGenerator/Extractor.extractAll.file.comments.phpt b/tests/PhpGenerator/Extractor.extractAll.file.comments.phpt new file mode 100644 index 00000000..03eafa44 --- /dev/null +++ b/tests/PhpGenerator/Extractor.extractAll.file.comments.phpt @@ -0,0 +1,43 @@ +extractAll(); + +Assert::null($file->getComment()); +Assert::same('doc comment', $file->getClasses()['Class1']->getComment()); + + +$file = (new Extractor(<<<'XX' + extractAll(); + +Assert::same('doc comment', $file->getComment()); + + +$file = (new Extractor(<<<'XX' + extractAll(); + +Assert::null($file->getComment()); From d0c7e3e59426d839fe5f2d9d8f929caf8efb5a6f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 28 Nov 2024 20:09:01 +0100 Subject: [PATCH 4/5] improved phpDoc --- src/PhpGenerator/Attribute.php | 2 +- src/PhpGenerator/ClassLike.php | 2 +- src/PhpGenerator/ClassType.php | 2 +- src/PhpGenerator/Closure.php | 2 +- src/PhpGenerator/Constant.php | 2 +- src/PhpGenerator/EnumCase.php | 2 +- src/PhpGenerator/EnumType.php | 2 +- src/PhpGenerator/GlobalFunction.php | 2 +- src/PhpGenerator/InterfaceType.php | 2 +- src/PhpGenerator/Method.php | 2 +- src/PhpGenerator/Parameter.php | 2 +- src/PhpGenerator/PhpFile.php | 2 +- src/PhpGenerator/PhpNamespace.php | 2 +- src/PhpGenerator/PromotedParameter.php | 2 +- src/PhpGenerator/Property.php | 2 +- src/PhpGenerator/TraitType.php | 2 +- src/PhpGenerator/TraitUse.php | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/PhpGenerator/Attribute.php b/src/PhpGenerator/Attribute.php index bd112c5e..ddea1df5 100644 --- a/src/PhpGenerator/Attribute.php +++ b/src/PhpGenerator/Attribute.php @@ -13,7 +13,7 @@ /** - * PHP Attribute. + * Definition of a PHP attribute. */ final class Attribute { diff --git a/src/PhpGenerator/ClassLike.php b/src/PhpGenerator/ClassLike.php index 6d9c1e8b..9e308e9d 100644 --- a/src/PhpGenerator/ClassLike.php +++ b/src/PhpGenerator/ClassLike.php @@ -13,7 +13,7 @@ /** - * Class/Interface/Trait/Enum description. + * Base definition of class, interface, trait or enum type. */ abstract class ClassLike { diff --git a/src/PhpGenerator/ClassType.php b/src/PhpGenerator/ClassType.php index 1877d163..1982e22a 100644 --- a/src/PhpGenerator/ClassType.php +++ b/src/PhpGenerator/ClassType.php @@ -13,7 +13,7 @@ /** - * Class description. + * Definition of a class with properties, methods, constants, traits and PHP attributes. * * @property-deprecated Method[] $methods * @property-deprecated Property[] $properties diff --git a/src/PhpGenerator/Closure.php b/src/PhpGenerator/Closure.php index 398e1491..1bacb2eb 100644 --- a/src/PhpGenerator/Closure.php +++ b/src/PhpGenerator/Closure.php @@ -13,7 +13,7 @@ /** - * Closure. + * Definition of a closure. * * @property-deprecated string $body */ diff --git a/src/PhpGenerator/Constant.php b/src/PhpGenerator/Constant.php index 8097dba5..8fbf5c42 100644 --- a/src/PhpGenerator/Constant.php +++ b/src/PhpGenerator/Constant.php @@ -13,7 +13,7 @@ /** - * Class constant. + * Definition of a class constant. */ final class Constant { diff --git a/src/PhpGenerator/EnumCase.php b/src/PhpGenerator/EnumCase.php index 226fd090..a7886e2e 100644 --- a/src/PhpGenerator/EnumCase.php +++ b/src/PhpGenerator/EnumCase.php @@ -13,7 +13,7 @@ /** - * Enum case. + * Definition of an enum case. */ final class EnumCase { diff --git a/src/PhpGenerator/EnumType.php b/src/PhpGenerator/EnumType.php index 8122a2ad..7fc09a11 100644 --- a/src/PhpGenerator/EnumType.php +++ b/src/PhpGenerator/EnumType.php @@ -13,7 +13,7 @@ /** - * Enum description. + * Definition of an enum with cases, methods, constants and traits. */ final class EnumType extends ClassLike { diff --git a/src/PhpGenerator/GlobalFunction.php b/src/PhpGenerator/GlobalFunction.php index ae1e4f88..09581c51 100644 --- a/src/PhpGenerator/GlobalFunction.php +++ b/src/PhpGenerator/GlobalFunction.php @@ -13,7 +13,7 @@ /** - * Global function. + * Definition of a global function. * * @property-deprecated string $body */ diff --git a/src/PhpGenerator/InterfaceType.php b/src/PhpGenerator/InterfaceType.php index 5a2d61d3..72f2b562 100644 --- a/src/PhpGenerator/InterfaceType.php +++ b/src/PhpGenerator/InterfaceType.php @@ -13,7 +13,7 @@ /** - * Interface description. + * Definition of an interface with methods and constants. * * @property-deprecated Method[] $methods */ diff --git a/src/PhpGenerator/Method.php b/src/PhpGenerator/Method.php index 0596658c..55b020c5 100644 --- a/src/PhpGenerator/Method.php +++ b/src/PhpGenerator/Method.php @@ -13,7 +13,7 @@ /** - * Class method. + * Definition of a class method. * * @property-deprecated string|null $body */ diff --git a/src/PhpGenerator/Parameter.php b/src/PhpGenerator/Parameter.php index 975d0449..51f0beb6 100644 --- a/src/PhpGenerator/Parameter.php +++ b/src/PhpGenerator/Parameter.php @@ -14,7 +14,7 @@ /** - * Function/Method parameter description. + * Definition of a function/method parameter. * * @property-deprecated mixed $defaultValue */ diff --git a/src/PhpGenerator/PhpFile.php b/src/PhpGenerator/PhpFile.php index 34142f5a..79947c1e 100644 --- a/src/PhpGenerator/PhpFile.php +++ b/src/PhpGenerator/PhpFile.php @@ -13,7 +13,7 @@ /** - * Instance of PHP file. + * Definition of a PHP file. * * Generates: * - opening tag ( Date: Fri, 13 Oct 2023 20:59:11 +0200 Subject: [PATCH 5/5] added Visibility --- src/PhpGenerator/ClassLike.php | 20 ++++++------ src/PhpGenerator/Extractor.php | 6 ++-- src/PhpGenerator/Factory.php | 4 +-- src/PhpGenerator/Method.php | 2 +- src/PhpGenerator/Traits/VisibilityAware.php | 27 ++++++---------- src/PhpGenerator/Visibility.php | 34 +++++++++++++++++++++ tests/PhpGenerator/ClassType.phpt | 3 +- 7 files changed, 60 insertions(+), 36 deletions(-) create mode 100644 src/PhpGenerator/Visibility.php diff --git a/src/PhpGenerator/ClassLike.php b/src/PhpGenerator/ClassLike.php index 9e308e9d..9e5469d4 100644 --- a/src/PhpGenerator/ClassLike.php +++ b/src/PhpGenerator/ClassLike.php @@ -21,19 +21,17 @@ abstract class ClassLike use Traits\CommentAware; use Traits\AttributeAware; - public const - VisibilityPublic = 'public', - VisibilityProtected = 'protected', - VisibilityPrivate = 'private'; + /** @deprecated use Visibility::Public */ + public const VisibilityPublic = Visibility::Public, + VISIBILITY_PUBLIC = Visibility::Public; - /** @deprecated use ClassLike::VisibilityPublic */ - public const VISIBILITY_PUBLIC = self::VisibilityPublic; + /** @deprecated use Visibility::Protected */ + public const VisibilityProtected = Visibility::Protected, + VISIBILITY_PROTECTED = Visibility::Protected; - /** @deprecated use ClassLike::VisibilityProtected */ - public const VISIBILITY_PROTECTED = self::VisibilityProtected; - - /** @deprecated use ClassLike::VisibilityPrivate */ - public const VISIBILITY_PRIVATE = self::VisibilityPrivate; + /** @deprecated use Visibility::Private */ + public const VisibilityPrivate = Visibility::Private, + VISIBILITY_PRIVATE = Visibility::Private; private ?PhpNamespace $namespace; private ?string $name; diff --git a/src/PhpGenerator/Extractor.php b/src/PhpGenerator/Extractor.php index 6b00ad49..2bb32624 100644 --- a/src/PhpGenerator/Extractor.php +++ b/src/PhpGenerator/Extractor.php @@ -428,9 +428,9 @@ private function formatValue(Node\Expr $value, int $level): Literal private function toVisibility(int $flags): ?string { return match (true) { - (bool) ($flags & Node\Stmt\Class_::MODIFIER_PUBLIC) => ClassType::VisibilityPublic, - (bool) ($flags & Node\Stmt\Class_::MODIFIER_PROTECTED) => ClassType::VisibilityProtected, - (bool) ($flags & Node\Stmt\Class_::MODIFIER_PRIVATE) => ClassType::VisibilityPrivate, + (bool) ($flags & Node\Stmt\Class_::MODIFIER_PUBLIC) => Visibility::Public, + (bool) ($flags & Node\Stmt\Class_::MODIFIER_PROTECTED) => Visibility::Protected, + (bool) ($flags & Node\Stmt\Class_::MODIFIER_PRIVATE) => Visibility::Private, default => null, }; } diff --git a/src/PhpGenerator/Factory.php b/src/PhpGenerator/Factory.php index 589c4b64..7a187767 100644 --- a/src/PhpGenerator/Factory.php +++ b/src/PhpGenerator/Factory.php @@ -310,8 +310,8 @@ private function getAttributes($from): array private function getVisibility($from): string { return $from->isPrivate() - ? ClassLike::VisibilityPrivate - : ($from->isProtected() ? ClassLike::VisibilityProtected : ClassLike::VisibilityPublic); + ? Visibility::Private + : ($from->isProtected() ? Visibility::Protected : Visibility::Public); } diff --git a/src/PhpGenerator/Method.php b/src/PhpGenerator/Method.php index 55b020c5..5dbd4c3e 100644 --- a/src/PhpGenerator/Method.php +++ b/src/PhpGenerator/Method.php @@ -99,7 +99,7 @@ public function addPromotedParameter(string $name, mixed $defaultValue = null): /** @throws Nette\InvalidStateException */ public function validate(): void { - if ($this->abstract && ($this->final || $this->visibility === ClassLike::VisibilityPrivate)) { + if ($this->abstract && ($this->final || $this->visibility === Visibility::Private)) { throw new Nette\InvalidStateException("Method $this->name() cannot be abstract and final or private at the same time."); } } diff --git a/src/PhpGenerator/Traits/VisibilityAware.php b/src/PhpGenerator/Traits/VisibilityAware.php index 0b3230ed..0a3a383f 100644 --- a/src/PhpGenerator/Traits/VisibilityAware.php +++ b/src/PhpGenerator/Traits/VisibilityAware.php @@ -9,8 +9,7 @@ namespace Nette\PhpGenerator\Traits; -use Nette; -use Nette\PhpGenerator\ClassLike; +use Nette\PhpGenerator\Visibility; /** @@ -22,16 +21,10 @@ trait VisibilityAware private ?string $visibility = null; - /** - * @param string|null $val public|protected|private - */ - public function setVisibility(?string $val): static + /** @param 'public'|'protected'|'private'|null $value */ + public function setVisibility(?string $value): static { - if (!in_array($val, [ClassLike::VisibilityPublic, ClassLike::VisibilityProtected, ClassLike::VisibilityPrivate, null], true)) { - throw new Nette\InvalidArgumentException('Argument must be public|protected|private.'); - } - - $this->visibility = $val; + $this->visibility = $value === null ? $value : Visibility::from($value); return $this; } @@ -44,39 +37,39 @@ public function getVisibility(): ?string public function setPublic(): static { - $this->visibility = ClassLike::VisibilityPublic; + $this->visibility = Visibility::Public; return $this; } public function isPublic(): bool { - return $this->visibility === ClassLike::VisibilityPublic || $this->visibility === null; + return $this->visibility === Visibility::Public || $this->visibility === null; } public function setProtected(): static { - $this->visibility = ClassLike::VisibilityProtected; + $this->visibility = Visibility::Protected; return $this; } public function isProtected(): bool { - return $this->visibility === ClassLike::VisibilityProtected; + return $this->visibility === Visibility::Protected; } public function setPrivate(): static { - $this->visibility = ClassLike::VisibilityPrivate; + $this->visibility = Visibility::Private; return $this; } public function isPrivate(): bool { - return $this->visibility === ClassLike::VisibilityPrivate; + return $this->visibility === Visibility::Private; } } diff --git a/src/PhpGenerator/Visibility.php b/src/PhpGenerator/Visibility.php new file mode 100644 index 00000000..a31c75c8 --- /dev/null +++ b/src/PhpGenerator/Visibility.php @@ -0,0 +1,34 @@ +getParameters()); Assert::exception( fn() => (new ClassType)->addMethod('method')->setVisibility('unknown'), - Nette\InvalidArgumentException::class, - 'Argument must be public|protected|private.', + ValueError::class, );