From 9c1d184025e133b9800e21598ed756674e6651c2 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 20 Dec 2022 01:13:22 +0100 Subject: [PATCH 1/9] updated .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 3aa6270a..9670e954 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,7 @@ .gitattributes export-ignore .gitignore export-ignore .github export-ignore -.travis.yml export-ignore -ecs.php export-ignore +ncs.* export-ignore phpstan.neon export-ignore tests/ export-ignore From d7195a2c7b32a2b53c9651af231adc5d13320a72 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 4 Oct 2022 04:09:58 +0200 Subject: [PATCH 2/9] updated phpstan baseline --- phpstan-baseline.neon | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index dd03b4fa..c826bc8e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Match expression does not handle remaining value\: true$#' - count: 1 - path: src/PhpGenerator/ClassLike.php - - message: '#^Method Nette\\PhpGenerator\\ClassType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#' count: 1 @@ -15,16 +10,6 @@ parameters: count: 1 path: src/PhpGenerator/EnumType.php - - - message: '#^Access to an undefined property PhpParser\\Node\:\:\$attrGroups\.$#' - count: 1 - path: src/PhpGenerator/Extractor.php - - - - message: '#^Access to an undefined property PhpParser\\Node\\Expr\:\:\$value\.$#' - count: 1 - path: src/PhpGenerator/Extractor.php - - message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addConstant\(\)\.$#' count: 1 @@ -45,13 +30,18 @@ parameters: count: 1 path: src/PhpGenerator/Extractor.php + - + message: '#^Call to an undefined method Nette\\PhpGenerator\\FunctionLike\:\:addPromotedParameter\(\)\.$#' + count: 1 + path: src/PhpGenerator/Extractor.php + - message: '#^Method Nette\\PhpGenerator\\Extractor\:\:addCommentAndAttributes\(\) has parameter \$element with no type specified\.$#' count: 1 path: src/PhpGenerator/Extractor.php - - message: '#^Property class@anonymous/PhpGenerator/Extractor\.php\:176\:\:\$callback has no type specified\.$#' + message: '#^Property class@anonymous/PhpGenerator/Extractor\.php\:175\:\:\$callback has no type specified\.$#' count: 1 path: src/PhpGenerator/Extractor.php @@ -65,6 +55,11 @@ parameters: count: 1 path: src/PhpGenerator/Factory.php + - + message: '#^Call to an undefined method ReflectionClass\\:\:isReadOnly\(\)\.$#' + count: 1 + path: src/PhpGenerator/Factory.php + - message: '#^Elseif branch is unreachable because previous condition is always true\.$#' count: 1 @@ -90,11 +85,6 @@ parameters: count: 1 path: src/PhpGenerator/Factory.php - - - message: '#^Parameter \#1 \$name of method Nette\\PhpGenerator\\ClassType\:\:setExtends\(\) expects string\|null, array\ given\.$#' - count: 1 - path: src/PhpGenerator/Factory.php - - message: '#^Unreachable statement \- code above always terminates\.$#' count: 1 @@ -115,6 +105,11 @@ parameters: count: 1 path: src/PhpGenerator/Method.php + - + message: '#^Method Nette\\PhpGenerator\\Printer\:\:printDocComment\(\) has parameter \$commentable with no type specified\.$#' + count: 1 + path: src/PhpGenerator/Printer.php + - message: '#^Method Nette\\PhpGenerator\\TraitType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#' count: 1 From 4b0ebe2bbb475433d45fe737f7bd96ee7a49398a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 4 Oct 2022 01:50:48 +0200 Subject: [PATCH 3/9] FunctionLike changed to abstract class --- src/PhpGenerator/Closure.php | 8 +------- src/PhpGenerator/Extractor.php | 2 +- src/PhpGenerator/Factory.php | 2 +- src/PhpGenerator/{Traits => }/FunctionLike.php | 12 +++++++----- src/PhpGenerator/GlobalFunction.php | 8 +------- src/PhpGenerator/Method.php | 12 ++++++------ src/PhpGenerator/Printer.php | 2 +- 7 files changed, 18 insertions(+), 28 deletions(-) rename src/PhpGenerator/{Traits => }/FunctionLike.php (94%) diff --git a/src/PhpGenerator/Closure.php b/src/PhpGenerator/Closure.php index 398e1491..43f26e1f 100644 --- a/src/PhpGenerator/Closure.php +++ b/src/PhpGenerator/Closure.php @@ -9,18 +9,12 @@ namespace Nette\PhpGenerator; -use Nette; - /** * Closure. - * - * @property-deprecated string $body */ -final class Closure +final class Closure extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\AttributeAware; /** @var Parameter[] */ diff --git a/src/PhpGenerator/Extractor.php b/src/PhpGenerator/Extractor.php index 0be2d2d8..836853c3 100644 --- a/src/PhpGenerator/Extractor.php +++ b/src/PhpGenerator/Extractor.php @@ -385,7 +385,7 @@ private function addCommentAndAttributes($element, Node $node): void } - private function setupFunction(GlobalFunction|Method $function, Node\FunctionLike $node): void + private function setupFunction(FunctionLike $function, Node\FunctionLike $node): void { $function->setReturnReference($node->returnsByRef()); $function->setReturnType($node->getReturnType() ? $this->toPhp($node->getReturnType()) : null); diff --git a/src/PhpGenerator/Factory.php b/src/PhpGenerator/Factory.php index 33c6f115..c8193a59 100644 --- a/src/PhpGenerator/Factory.php +++ b/src/PhpGenerator/Factory.php @@ -194,7 +194,7 @@ public function fromFunctionReflection(\ReflectionFunction $from, bool $withBody } - public function fromCallable(callable $from): Method|GlobalFunction|Closure + public function fromCallable(callable $from): FunctionLike { $ref = Nette\Utils\Callback::toReflection($from); return $ref instanceof \ReflectionMethod diff --git a/src/PhpGenerator/Traits/FunctionLike.php b/src/PhpGenerator/FunctionLike.php similarity index 94% rename from src/PhpGenerator/Traits/FunctionLike.php rename to src/PhpGenerator/FunctionLike.php index 15bb8e57..7f808255 100644 --- a/src/PhpGenerator/Traits/FunctionLike.php +++ b/src/PhpGenerator/FunctionLike.php @@ -7,19 +7,21 @@ declare(strict_types=1); -namespace Nette\PhpGenerator\Traits; +namespace Nette\PhpGenerator; use Nette; -use Nette\PhpGenerator\Dumper; -use Nette\PhpGenerator\Parameter; use Nette\Utils\Type; /** - * @internal + * GlobalFunction/Closure/Method description. + * + * @property-deprecated string $body */ -trait FunctionLike +abstract class FunctionLike { + use Nette\SmartObject; + private string $body = ''; /** @var Parameter[] */ diff --git a/src/PhpGenerator/GlobalFunction.php b/src/PhpGenerator/GlobalFunction.php index ae1e4f88..76393715 100644 --- a/src/PhpGenerator/GlobalFunction.php +++ b/src/PhpGenerator/GlobalFunction.php @@ -9,18 +9,12 @@ namespace Nette\PhpGenerator; -use Nette; - /** * Global function. - * - * @property-deprecated string $body */ -final class GlobalFunction +final class GlobalFunction extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\NameAware; use Traits\CommentAware; use Traits\AttributeAware; diff --git a/src/PhpGenerator/Method.php b/src/PhpGenerator/Method.php index 0596658c..2c636a48 100644 --- a/src/PhpGenerator/Method.php +++ b/src/PhpGenerator/Method.php @@ -14,13 +14,9 @@ /** * Class method. - * - * @property-deprecated string|null $body */ -final class Method +final class Method extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\NameAware; use Traits\VisibilityAware; use Traits\CommentAware; @@ -92,7 +88,11 @@ public function addPromotedParameter(string $name, mixed $defaultValue = null): $param->setDefaultValue($defaultValue); } - return $this->parameters[$name] = $param; + $params = $this->getParameters(); + $params[$name] = $param; + $this->setParameters($params); + + return $param; } diff --git a/src/PhpGenerator/Printer.php b/src/PhpGenerator/Printer.php index c7005fe1..4e0f3ee3 100644 --- a/src/PhpGenerator/Printer.php +++ b/src/PhpGenerator/Printer.php @@ -385,7 +385,7 @@ protected function printDocComment(/*Traits\CommentAware*/ $commentable): string } - private function printReturnType(Closure|GlobalFunction|Method $function): string + private function printReturnType(FunctionLike $function): string { return ($tmp = $this->printType($function->getReturnType(), $function->isReturnNullable())) ? $this->returnTypeColon . $tmp From 42fed7dcc540f4ff103e8503f5b7794ca84b8eb3 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 5 Dec 2022 01:55:53 +0100 Subject: [PATCH 4/9] better deprecation messages --- src/PhpGenerator/ClassLike.php | 13 ++++--- src/PhpGenerator/PhpNamespace.php | 13 ++++--- src/PhpGenerator/Type.php | 65 ++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/PhpGenerator/ClassLike.php b/src/PhpGenerator/ClassLike.php index 8df107dc..6d9c1e8b 100644 --- a/src/PhpGenerator/ClassLike.php +++ b/src/PhpGenerator/ClassLike.php @@ -26,11 +26,14 @@ abstract class ClassLike VisibilityProtected = 'protected', VisibilityPrivate = 'private'; - /** @deprecated */ - public const - VISIBILITY_PUBLIC = self::VisibilityPublic, - VISIBILITY_PROTECTED = self::VisibilityProtected, - VISIBILITY_PRIVATE = self::VisibilityPrivate; + /** @deprecated use ClassLike::VisibilityPublic */ + public const VISIBILITY_PUBLIC = self::VisibilityPublic; + + /** @deprecated use ClassLike::VisibilityProtected */ + public const VISIBILITY_PROTECTED = self::VisibilityProtected; + + /** @deprecated use ClassLike::VisibilityPrivate */ + public const VISIBILITY_PRIVATE = self::VisibilityPrivate; private ?PhpNamespace $namespace; private ?string $name; diff --git a/src/PhpGenerator/PhpNamespace.php b/src/PhpGenerator/PhpNamespace.php index 660ff026..0303d2dd 100644 --- a/src/PhpGenerator/PhpNamespace.php +++ b/src/PhpGenerator/PhpNamespace.php @@ -30,11 +30,14 @@ final class PhpNamespace NameFunction = 'f', NameConstant = 'c'; - /** @deprecated */ - public const - NAME_NORMAL = self::NameNormal, - NAME_FUNCTION = self::NameFunction, - NAME_CONSTANT = self::NameConstant; + /** @deprecated use PhpNamespace::NameNormal */ + public const NAME_NORMAL = self::NameNormal; + + /** @deprecated use PhpNamespace::NameFunction */ + public const NAME_FUNCTION = self::NameFunction; + + /** @deprecated use PhpNamespace::NameConstant */ + public const NAME_CONSTANT = self::NameConstant; private string $name; diff --git a/src/PhpGenerator/Type.php b/src/PhpGenerator/Type.php index be3242a4..9abd87cd 100644 --- a/src/PhpGenerator/Type.php +++ b/src/PhpGenerator/Type.php @@ -34,24 +34,53 @@ class Type Parent = 'parent', Static = 'static'; - /** @deprecated */ - public const - STRING = self::String, - INT = self::Int, - FLOAT = self::Float, - BOOL = self::Bool, - ARRAY = self::Array, - OBJECT = self::Object, - CALLABLE = self::Callable, - ITERABLE = self::Iterable, - VOID = self::Void, - NEVER = self::Never, - MIXED = self::Mixed, - FALSE = self::False, - NULL = self::Null, - SELF = self::Self, - PARENT = self::Parent, - STATIC = self::Static; + /** @deprecated use Type::String */ + public const STRING = self::String; + + /** @deprecated use Type::Int */ + public const INT = self::Int; + + /** @deprecated use Type::Float */ + public const FLOAT = self::Float; + + /** @deprecated use Type::Bool */ + public const BOOL = self::Bool; + + /** @deprecated use Type::Array */ + public const ARRAY = self::Array; + + /** @deprecated use Type::Object */ + public const OBJECT = self::Object; + + /** @deprecated use Type::Callable */ + public const CALLABLE = self::Callable; + + /** @deprecated use Type::Iterable */ + public const ITERABLE = self::Iterable; + + /** @deprecated use Type::Void */ + public const VOID = self::Void; + + /** @deprecated use Type::Never */ + public const NEVER = self::Never; + + /** @deprecated use Type::Mixed */ + public const MIXED = self::Mixed; + + /** @deprecated use Type::False */ + public const FALSE = self::False; + + /** @deprecated use Type::Null */ + public const NULL = self::Null; + + /** @deprecated use Type::Self */ + public const SELF = self::Self; + + /** @deprecated use Type::Parent */ + public const PARENT = self::Parent; + + /** @deprecated use Type::Static */ + public const STATIC = self::Static; public static function nullable(string $type, bool $state = true): string From 3ff4881b0ddff1c9f3050fa8c1e3a4d61adde2b6 Mon Sep 17 00:00:00 2001 From: Arczi Date: Sat, 7 Jan 2023 00:07:31 +0100 Subject: [PATCH 5/9] =?UTF-8?q?Made=20Printer::printAttributes=20protected?= =?UTF-8?q?=20(was=20private)=20to=20enable=20possibility=20of=20changing?= =?UTF-8?q?=20its=20behaviour=20by=20user=20(Open=E2=80=93closed=20princip?= =?UTF-8?q?le)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/PhpGenerator/Printer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpGenerator/Printer.php b/src/PhpGenerator/Printer.php index 4e0f3ee3..f3d87caa 100644 --- a/src/PhpGenerator/Printer.php +++ b/src/PhpGenerator/Printer.php @@ -394,7 +394,7 @@ private function printReturnType(FunctionLike $function): string /** @param Attribute[] $attrs */ - private function printAttributes(array $attrs, bool $inline = false): string + protected function printAttributes(array $attrs, bool $inline = false): string { if (!$attrs) { return ''; From da995cae0040bf1c2da113678e88142e1f3333b9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 10 Jan 2023 01:34:14 +0100 Subject: [PATCH 6/9] used PhpStorm Language attribute --- composer.json | 3 ++- src/PhpGenerator/Traits/FunctionLike.php | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index fc193a79..25dac358 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "nette/tester": "^2.4", "nikic/php-parser": "^4.15", "tracy/tracy": "^2.8", - "phpstan/phpstan": "^1.0" + "phpstan/phpstan": "^1.0", + "jetbrains/phpstorm-attributes": "dev-master" }, "suggest": { "nikic/php-parser": "to use ClassType::from(withBodies: true) & ClassType::fromCode()" diff --git a/src/PhpGenerator/Traits/FunctionLike.php b/src/PhpGenerator/Traits/FunctionLike.php index 15bb8e57..c0b33f99 100644 --- a/src/PhpGenerator/Traits/FunctionLike.php +++ b/src/PhpGenerator/Traits/FunctionLike.php @@ -9,6 +9,7 @@ namespace Nette\PhpGenerator\Traits; +use JetBrains\PhpStorm\Language; use Nette; use Nette\PhpGenerator\Dumper; use Nette\PhpGenerator\Parameter; @@ -31,7 +32,11 @@ trait FunctionLike /** @param ?mixed[] $args */ - public function setBody(string $code, ?array $args = null): static + public function setBody( + #[Language('PHP')] + string $code, + ?array $args = null, + ): static { $this->body = $args === null ? $code @@ -47,7 +52,11 @@ public function getBody(): string /** @param ?mixed[] $args */ - public function addBody(string $code, ?array $args = null): static + public function addBody( + #[Language('PHP')] + string $code, + ?array $args = null, + ): static { $this->body .= ($args === null ? $code : (new Dumper)->format($code, ...$args)) . "\n"; return $this; From a580c500a727962332116244223e88f99e63d6bf Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 4 Oct 2022 01:50:48 +0200 Subject: [PATCH 7/9] FunctionLike changed to abstract class --- src/PhpGenerator/Closure.php | 8 +------- src/PhpGenerator/Extractor.php | 2 +- src/PhpGenerator/Factory.php | 2 +- src/PhpGenerator/{Traits => }/FunctionLike.php | 12 +++++++----- src/PhpGenerator/GlobalFunction.php | 8 +------- src/PhpGenerator/Method.php | 12 ++++++------ src/PhpGenerator/Printer.php | 2 +- 7 files changed, 18 insertions(+), 28 deletions(-) rename src/PhpGenerator/{Traits => }/FunctionLike.php (94%) diff --git a/src/PhpGenerator/Closure.php b/src/PhpGenerator/Closure.php index 398e1491..43f26e1f 100644 --- a/src/PhpGenerator/Closure.php +++ b/src/PhpGenerator/Closure.php @@ -9,18 +9,12 @@ namespace Nette\PhpGenerator; -use Nette; - /** * Closure. - * - * @property-deprecated string $body */ -final class Closure +final class Closure extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\AttributeAware; /** @var Parameter[] */ diff --git a/src/PhpGenerator/Extractor.php b/src/PhpGenerator/Extractor.php index 0be2d2d8..836853c3 100644 --- a/src/PhpGenerator/Extractor.php +++ b/src/PhpGenerator/Extractor.php @@ -385,7 +385,7 @@ private function addCommentAndAttributes($element, Node $node): void } - private function setupFunction(GlobalFunction|Method $function, Node\FunctionLike $node): void + private function setupFunction(FunctionLike $function, Node\FunctionLike $node): void { $function->setReturnReference($node->returnsByRef()); $function->setReturnType($node->getReturnType() ? $this->toPhp($node->getReturnType()) : null); diff --git a/src/PhpGenerator/Factory.php b/src/PhpGenerator/Factory.php index 33c6f115..c8193a59 100644 --- a/src/PhpGenerator/Factory.php +++ b/src/PhpGenerator/Factory.php @@ -194,7 +194,7 @@ public function fromFunctionReflection(\ReflectionFunction $from, bool $withBody } - public function fromCallable(callable $from): Method|GlobalFunction|Closure + public function fromCallable(callable $from): FunctionLike { $ref = Nette\Utils\Callback::toReflection($from); return $ref instanceof \ReflectionMethod diff --git a/src/PhpGenerator/Traits/FunctionLike.php b/src/PhpGenerator/FunctionLike.php similarity index 94% rename from src/PhpGenerator/Traits/FunctionLike.php rename to src/PhpGenerator/FunctionLike.php index c0b33f99..7d6d0879 100644 --- a/src/PhpGenerator/Traits/FunctionLike.php +++ b/src/PhpGenerator/FunctionLike.php @@ -7,20 +7,22 @@ declare(strict_types=1); -namespace Nette\PhpGenerator\Traits; +namespace Nette\PhpGenerator; use JetBrains\PhpStorm\Language; use Nette; -use Nette\PhpGenerator\Dumper; -use Nette\PhpGenerator\Parameter; use Nette\Utils\Type; /** - * @internal + * GlobalFunction/Closure/Method description. + * + * @property-deprecated string $body */ -trait FunctionLike +abstract class FunctionLike { + use Nette\SmartObject; + private string $body = ''; /** @var Parameter[] */ diff --git a/src/PhpGenerator/GlobalFunction.php b/src/PhpGenerator/GlobalFunction.php index ae1e4f88..76393715 100644 --- a/src/PhpGenerator/GlobalFunction.php +++ b/src/PhpGenerator/GlobalFunction.php @@ -9,18 +9,12 @@ namespace Nette\PhpGenerator; -use Nette; - /** * Global function. - * - * @property-deprecated string $body */ -final class GlobalFunction +final class GlobalFunction extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\NameAware; use Traits\CommentAware; use Traits\AttributeAware; diff --git a/src/PhpGenerator/Method.php b/src/PhpGenerator/Method.php index 0596658c..2c636a48 100644 --- a/src/PhpGenerator/Method.php +++ b/src/PhpGenerator/Method.php @@ -14,13 +14,9 @@ /** * Class method. - * - * @property-deprecated string|null $body */ -final class Method +final class Method extends FunctionLike { - use Nette\SmartObject; - use Traits\FunctionLike; use Traits\NameAware; use Traits\VisibilityAware; use Traits\CommentAware; @@ -92,7 +88,11 @@ public function addPromotedParameter(string $name, mixed $defaultValue = null): $param->setDefaultValue($defaultValue); } - return $this->parameters[$name] = $param; + $params = $this->getParameters(); + $params[$name] = $param; + $this->setParameters($params); + + return $param; } diff --git a/src/PhpGenerator/Printer.php b/src/PhpGenerator/Printer.php index c7005fe1..4e0f3ee3 100644 --- a/src/PhpGenerator/Printer.php +++ b/src/PhpGenerator/Printer.php @@ -385,7 +385,7 @@ protected function printDocComment(/*Traits\CommentAware*/ $commentable): string } - private function printReturnType(Closure|GlobalFunction|Method $function): string + private function printReturnType(FunctionLike $function): string { return ($tmp = $this->printType($function->getReturnType(), $function->isReturnNullable())) ? $this->returnTypeColon . $tmp From 04831bf6a7d3ea155be0facf49bb3ed483d64708 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 17 Jan 2023 21:01:00 +0100 Subject: [PATCH 8/9] typo --- src/PhpGenerator/Helpers.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/PhpGenerator/Helpers.php b/src/PhpGenerator/Helpers.php index e49db879..3cd4a10c 100644 --- a/src/PhpGenerator/Helpers.php +++ b/src/PhpGenerator/Helpers.php @@ -25,7 +25,7 @@ final class Helpers // built-in types 'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1, 'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1, - 'never' => 1, + 'never' => 1, 'true' => 1, // class keywords 'self' => 1, 'parent' => 1, 'static' => 1, @@ -42,9 +42,6 @@ final class Helpers '__TRAIT__' => 1, '__FUNCTION__' => 1, '__METHOD__' => 1, '__LINE__' => 1, '__FILE__' => 1, '__DIR__' => 1, '__NAMESPACE__' => 1, 'fn' => 1, 'match' => 1, 'enum' => 1, 'abstract' => 1, 'final' => 1, 'private' => 1, 'protected' => 1, 'public' => 1, 'readonly' => 1, - - // additional reserved class names - 'true' => 1, ]; /** @deprecated */ From 74b68887ed5bcde40d214e3a2ac4a133ccfc99f1 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 19 Jan 2023 03:08:06 +0100 Subject: [PATCH 9/9] added docs --- .gitattributes | 1 + docs/cs/@home.texy | 823 +++++++++++++++++++++++++++++++++++++++++++++ docs/en/@home.texy | 822 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1646 insertions(+) create mode 100644 docs/cs/@home.texy create mode 100644 docs/en/@home.texy diff --git a/.gitattributes b/.gitattributes index 9670e954..d1b8bb64 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ .github export-ignore ncs.* export-ignore phpstan.neon export-ignore +docs/ export-ignore tests/ export-ignore *.sh eol=lf diff --git a/docs/cs/@home.texy b/docs/cs/@home.texy new file mode 100644 index 00000000..2d4d02c2 --- /dev/null +++ b/docs/cs/@home.texy @@ -0,0 +1,823 @@ +Generátor PHP kódu +****************** + +
+- Potřebujete generovat kód tříd, funkcí, PHP souborů atd? +- Umí všechny nejnovější vychytávky v PHP (jako enumy atd.) +- Dovolí vám snadno modifikovat existující třídy +- Výstup vyhovující PSR-12 +- Zralá, stabilní a široce používaná knihovna +
+ + +Instalace +--------- + +Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|/best-practices/composer]: + +```shell +composer require nette/php-generator +``` + +Kompatibilitu s PHP naleznete v [tabulce |#Tabulka kompatibility]. + + +Třídy +----- + +Začněme rovnou příkladem tvorby třídy pomocí [ClassType |api:Nette\PhpGenerator\ClassType]: + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); + +$class + ->setFinal() + ->setExtends(ParentClass::class) + ->addImplement(Countable::class) + ->addComment("Popis třídy.\nDruhý řádek\n") + ->addComment('@property-read Nette\Forms\Form $form'); + +// kód jednoduše vygenerujete přetypováním na řetězec nebo použitím echo: +echo $class; +``` + +Vrátí následující výsledek: + +```php +/** + * Popis třídy + * Druhý řádek + * + * @property-read Nette\Forms\Form $form + */ +final class Demo extends ParentClass implements Countable +{ + use Nette\SmartObject; +} +``` + +K vygenerování kódu můžeme také použít tzv. printer, který na rozdíl od `echo $class` budeme moci [dále konfigurovat |#Printer a soulad s PSR]: + +```php +$printer = new Nette\PhpGenerator\Printer; +echo $printer->printClass($class); +``` + +Můžeme přidat konstanty (třída [Constant |api:Nette\PhpGenerator\Constant]) a proměnné (třída [Property |api:Nette\PhpGenerator\Property]): + +```php +$class->addConstant('ID', 123) + ->setProtected() // viditelnost konstant + ->setFinal(); + +$class->addProperty('items', [1, 2, 3]) + ->setPrivate() // nebo setVisibility('private') + ->setStatic() + ->addComment('@var int[]'); + +$class->addProperty('list') + ->setType('?array') + ->setInitialized(); // vypíše '= null' +``` + +Vygeneruje: + +```php +final protected const ID = 123; + +/** @var int[] */ +private static $items = [1, 2, 3]; + +public ?array $list = null; +``` + +A můžeme přidat [metody|#Signatury metod a funkcí]: + +```php +$method = $class->addMethod('count') + ->addComment('Count it.') + ->setFinal() + ->setProtected() + ->setReturnType('?int') // návratové typy u metod + ->setBody('return count($items ?: $this->items);'); + +$method->addParameter('items', []) // $items = [] + ->setReference() // &$items = [] + ->setType('array'); // array &$items = [] +``` + +Výsledkem je: + +```php +/** + * Count it. + */ +final protected function count(array &$items = []): ?int +{ + return count($items ?: $this->items); +} +``` + +Propagované parametry zavedené PHP 8.0 lze předat konstruktoru (od verze 3.5): + +```php +$method = $class->addMethod('__construct'); +$method->addPromotedParameter('name'); +$method->addPromotedParameter('args', []) + ->setPrivate(); +``` + +Výsledkem je: + +```php +public function __construct( + public $name, + private $args = [], +) { +} +``` + +Vlastnosti určené pouze pro čtení zavedené v PHP 8.1 lze označit pomocí funkce `setReadOnly()`. + +------ + +Pokud přidaná vlastnost, konstanta, metoda nebo parametr již existují, vyhodí se výjimka. + +Členy třídy lze odebrat pomocí `removeProperty()`, `removeConstant()`, `removeMethod()` nebo `removeParameter()`. + +Do třídy můžete také přidat existující objekty `Method`, `Property` nebo `Constant`: + +```php +$method = new Nette\PhpGenerator\Method('getHandle'); +$property = new Nette\PhpGenerator\Property('handle'); +$const = new Nette\PhpGenerator\Constant('ROLE'); + +$class = (new Nette\PhpGenerator\ClassType('Demo')) + ->addMember($method) + ->addMember($property) + ->addMember($const); +``` + +Můžete také klonovat stávající metody, vlastnosti a konstanty pod jiným názvem pomocí `cloneWithName()`: + +```php +$methodCount = $class->getMethod('count'); +$methodRecount = $methodCount->cloneWithName('recount'); +$class->addMember($methodRecount); +``` + + +Interface nebo traita +--------------------- + +Lze vytvářet rozhraní a traity: + +```php +$interface = new Nette\PhpGenerator\InterfaceType('MyInterface'); +$trait = new Nette\PhpGenerator\TraitType('MyTrait'); +``` + +Používání trait: + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +$class->addTrait('SmartObject'); +$class->addTrait('MyTrait') + ->addResolution('sayHello as protected') + ->addComment('@use MyTrait'); +echo $class; +``` + +Výsledek: + +```php +class Demo +{ + use SmartObject; + use MyTrait { + sayHello as protected; + } +} +``` + + +Enums .{data-version:v3.6} +-------------------------- + +Výčty, které přináší PHP 8.1, můžete snadno vytvořit takto: + +```php +$enum = new Nette\PhpGenerator\EnumType('Suit'); +$enum->addCase('Clubs'); +$enum->addCase('Diamonds'); +$enum->addCase('Hearts'); +$enum->addCase('Spades'); + +echo $enum; +``` + +Výsledek: + +```php +enum Suit +{ + case Clubs; + case Diamonds; + case Hearts; + case Spades; +} +``` + +Můžete také definovat skalární ekvivalenty a vytvořit tak "backed" výčet: + +```php +$enum->addCase('Clubs', '♣'); +$enum->addCase('Diamonds', '♦'); +``` + +Ke každému *case* je možné přidat komentář nebo [#atributy] pomocí `addComment()` nebo `addAttribute()`. + + +Anonymní třídy +-------------- + +Jako název předáme `null` a máme anonymní třídu: + +```php +$class = new Nette\PhpGenerator\ClassType(null); +$class->addMethod('__construct') + ->addParameter('foo'); + +echo '$obj = new class ($val) ' . $class . ';'; +``` + +Výsledek: + +```php +$obj = new class ($val) { + + public function __construct($foo) + { + } +}; +``` + + +Globální funkce +--------------- + +Kód funkcí generuje třída [GlobalFunction |api:Nette\PhpGenerator\GlobalFunction]: + +```php +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->setBody('return $a + $b;'); +$function->addParameter('a'); +$function->addParameter('b'); +echo $function; + +// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function); +``` + +Výsledek: + +```php +function foo($a, $b) +{ + return $a + $b; +} +``` + + +Anonymní funkce +--------------- + +Kód anonymních funkcí generuje třída [Closure |api:Nette\PhpGenerator\Closure]: + +```php +$closure = new Nette\PhpGenerator\Closure; +$closure->setBody('return $a + $b;'); +$closure->addParameter('a'); +$closure->addParameter('b'); +$closure->addUse('c') + ->setReference(); +echo $closure; + +// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure); +``` + +Výsledek: + +```php +function ($a, $b) use (&$c) { + return $a + $b; +} +``` + + +Zkrácené arrow funkce +--------------------- + +Můžete také vypsat zkrácenou anonymní funkci pomocí printeru: + +```php +$closure = new Nette\PhpGenerator\Closure; +$closure->setBody('$a + $b'); +$closure->addParameter('a'); +$closure->addParameter('b'); + +echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure); +``` + +Výsledek: + +```php +fn($a, $b) => $a + $b +``` + + +Signatury metod a funkcí +------------------------ + +Metody reprezentuje třída [Method |api:Nette\PhpGenerator\Method]. Můžete nastavit viditelnost, návratovou hodnotu, přidat komentáře, [atributy|#Atributy] atd: + +```php +$method = $class->addMethod('count') + ->addComment('Count it.') + ->setFinal() + ->setProtected() + ->setReturnType('?int'); +``` + +Jednotlivé parametry reprezentuje třídy [Parameter |api:Nette\PhpGenerator\Parameter]. Opět můžete nastavit všechny myslitelné vlastnosti: + +```php +$method->addParameter('items', []) // $items = [] + ->setReference() // &$items = [] + ->setType('array'); // array &$items = [] + +// function count(&$items = []) +``` + +Pro definici tzv. variadics parametrů (nebo též splat operátor) slouží `setVariadics()`: + +```php +$method = $class->addMethod('count'); +$method->setVariadic(true); +$method->addParameter('items'); +``` + +Vygeneruje: + +```php +function count(...$items) +{ +} +``` + + +Těla metod a funkcí +------------------- + +Tělo lze předat najednou metodě `setBody()` nebo postupně (po řádcích) opakovaným voláním `addBody()`: + +```php +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addBody('$a = rand(10, 20);'); +$function->addBody('return $a;'); +echo $function; +``` + +Výsledek + +```php +function foo() +{ + $a = rand(10, 20); + return $a; +} +``` + +Můžete použít speciální zástupné znaky pro snadné vkládání proměnných. + +Jednoduché zástupné symboly `?` + +```php +$str = 'any string'; +$num = 3; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addBody('return substr(?, ?);', [$str, $num]); +echo $function; +``` + +Výsledek + +```php +function foo() +{ + return substr('any string', 3); +} +``` + +Zástupný znak pro variadic `...?` + +```php +$items = [1, 2, 3]; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->setBody('myfunc(...?);', [$items]); +echo $function; +``` + +Výsledek: + +```php +function foo() +{ + myfunc(1, 2, 3); +} +``` + +Můžete také použít pojmenované parametry pro PHP 8 pomocí `...?: `(od verze 3.5) + +```php +$items = ['foo' => 1, 'bar' => true]; +$function->setBody('myfunc(...?:);', [$items]); + +// myfunc(foo: 1, bar: true); +``` + +Zástupný symbol se escapuje pomocí lomítka `\?` + +```php +$num = 3; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addParameter('a'); +$function->addBody('return $a \? 10 : ?;', [$num]); +echo $function; +``` + +Výsledek: + +```php +function foo($a) +{ + return $a ? 10 : 3; +} +``` + + +Printer a soulad s PSR +---------------------- + +PHP kód generují objekty `Printer`. K je vám tiskárna `PsrPrinter`, jejíž výstup je v souladu s PSR-2 a PSR-12 a k odsazování používá mezery, a dále `Printer`, která pro odsazování používá tabulátory. + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +// ... + +$printer = new Nette\PhpGenerator\PsrPrinter; +echo $printer->printClass($class); // odsazení 4 mezerami +``` + +Potřebujete na míru upravit chování printeru? Vytvořte si vlastní poděděním třídy `Printer`. Můžete překonfigurovat tyto proměnné: + +```php +class MyPrinter extends Nette\PhpGenerator\Printer +{ + public int $wrapLength = 120; + public string $indentation = "\t"; + public int $linesBetweenProperties = 0; + public int $linesBetweenMethods = 2; + public int $linesBetweenUseTypes = 0; + public bool $bracesOnNextLine = true; + public string $returnTypeColon = ': '; +} +``` + + +Typy +---- + +Každý typ nebo union/intersection typ lze předat jako řetězec, můžete také použít předdefinované konstanty pro nativní typy: + +```php +use Nette\PhpGenerator\Type; + +$member->setType('array'); // nebo Type::ARRAY; +$member->setType('array|string'); // nebo Type::union('array', 'string') +$member->setType('Foo&Bar'); // nebo Type::intersection(Foo::class, Bar::class) +$member->setType(null); // odstraní typ +``` + +Totéž platí pro metodu `setReturnType()`. + + +Literály +-------- + +Pomocí `Literal` můžete předávat libovolný kód PHP, například pro výchozí hodnoty vlastností nebo parametrů atd: + +```php +use Nette\PhpGenerator\Literal; + +$class = new Nette\PhpGenerator\ClassType('Demo'); + +$class->addProperty('foo', new Literal('Iterator::SELF_FIRST')); + +$class->addMethod('bar') + ->addParameter('id', new Literal('1 + 2')); + +echo $class; +``` + +Výsledek: + +```php +class Demo +{ + public $foo = Iterator::SELF_FIRST; + + public function bar($id = 1 + 2) + { + } +} +``` + +Můžete také předat parametry do `Literal` a nechat je zformátovat do platného kódu PHP pomocí [zástupných znaků|#Generování těl metod a funkcí]: + +```php +new Literal('substr(?, ?)', [$a, $b]); +// generuje například: substr('hello', 5); +``` + + +Atributy .{data-version:v3.5} +----------------------------- + +PHP 8 atributy můžete přidat do všech tříd, metod, vlastností, konstant, enumů, funkcí, closures a parametrů. Jako hodnoty parametrů lze používat i [#literály]. + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +$class->addAttribute('Deprecated'); + +$class->addProperty('list') + ->addAttribute('WithArguments', [1, 2]); + +$method = $class->addMethod('count') + ->addAttribute('Foo\Cached', ['mode' => true]); + +$method->addParameter('items') + ->addAttribute('Bar'); + +echo $class; +``` + +Výsledek: + +```php +#[Deprecated] +class Demo +{ + #[WithArguments(1, 2)] + public $list; + + + #[Foo\Cached(mode: true)] + public function count(#[Bar] $items) + { + } +} +``` + + +Jmenný prostor +-------------- + +Třídy, vlastnosti, rozhraní a výčty (dále jen třídy) lze seskupit do jmenných prostorů reprezentovaných třídou [PhpNamespace |api:Nette\PhpGenerator\PhpNamespace]: + +```php +$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); + +// vytvoříme nové třídy v namespace +$class = $namespace->addClass('Task'); +$interface = $namespace->addInterface('Countable'); +$trait = $namespace->addTrait('NameAware'); + +// nebo vložíme existující třídu do namespace +$class = new Nette\PhpGenerator\ClassType('Task'); +$namespace->add($class); +``` + +Pokud třída již existuje, vyhodí se výjimka. + +Můžete definovat klauzule use: + +```php +// use Http\Request; +$namespace->addUse(Http\Request::class); +// use Http\Request as HttpReq; +$namespace->addUse(Http\Request::class, 'HttpReq'); +// use function iter\range; +$namespace->addUseFunction('iter\range'); +``` + +Chcete-li zjednodušit plně kvalifikovaný název třídy, funkce nebo konstanty podle definovaných aliasů, použijte metodu `simplifyName`: + +```php +echo $namespace->simplifyName('Foo\Bar'); // 'Bar', protože 'Foo' je aktuální jmenný prostor +echo $namespace->simplifyName('iter\range', $namespace::NAME_FUNCTION); // 'range', kvůli definovanému use-statement +``` + +Zjednodušený název třídy, funkce nebo konstanty můžete naopak převést na plně kvalifikovaný název pomocí metody `resolveName`: + +```php +echo $namespace->resolveName('Bar'); // 'Foo\Bar' +echo $namespace->resolveName('range', $namespace::NAME_FUNCTION); // 'iter\range' +``` + + +Překlady názvů tříd +------------------- + +**Když je třída součástí jmenného prostoru, je vykreslena mírně odlišně:** všechny typy (například typehinty, návratové typy, název rodičovské třídy, +implementovaná rozhraní, použité vlastnosti a atributy) jsou automaticky *překládány* (pokud to nevypnete, viz níže). +To znamená, že musíte v definicích **používat úplné názvy tříd** a ty budou nahrazeny +za aliasy (podle klauzulí use) nebo za plně kvalifikovaná jména ve výsledném kódu: + +```php +$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); +$namespace->addUse('Bar\AliasedClass'); + +$class = $namespace->addClass('Demo'); +$class->addImplement('Foo\A') // bude zjednodušen na A + ->addTrait('Bar\AliasedClass'); // bude zjednodušen na AliasedClass + +$method = $class->addMethod('method'); +$method->addComment('@return ' . $namespace->simplifyName('Foo\D')); // v komentářích zjednodušíme manuálně +$method->addParameter('arg') + ->setType('Bar\OtherClass'); // bude přeložen na \Bar\OtherClass + +echo $namespace; + +// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace); +``` + +Výsledek: + +```php +namespace Foo; + +use Bar\AliasedClass; + +class Demo implements A +{ + use AliasedClass; + + /** + * @return D + */ + public function method(\Bar\OtherClass $arg) + { + } +} +``` + +Automatické překládání lze vypnout tímto způsobem: + +```php +$printer = new Nette\PhpGenerator\Printer; // nebo PsrPrinter +$printer->setTypeResolving(false); +echo $printer->printNamespace($namespace); +``` + + +PHP soubory +----------- + +Třídy, funkce a jmenné prostory lze seskupit do PHP souborů reprezentovaných třídou [PhpFile|api:Nette\PhpGenerator\PhpFile]: + +```php +$file = new Nette\PhpGenerator\PhpFile; +$file->addComment('This file is auto-generated.'); +$file->setStrictTypes(); // přidá declare(strict_types=1) + +$class = $file->addClass('Foo\A'); +$function = $file->addFunction('Foo\foo'); + +// nebo +// $namespace = $file->addNamespace('Foo'); +// $class = $namespace->addClass('A'); +// $function = $namespace->addFunction('foo'); + +echo $file; + +// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file); +``` + +Výsledek: + +```php +dump($var); // vypíše ['a', 'b', 123] +``` + + +Tabulka kompatibility +--------------------- + +PhpGenerator 4.0 je kompatibilní s PHP 8.0 až 8.2 +PhpGenerator 3.6 je kompatibilní s PHP 7.2 až 8.2 +PhpGenerator 3.2 – 3.5 je kompatibilní s PHP 7.1 až 8.0 +PhpGenerator 3.1 je kompatibilní s PHP 7.1 až 7.3 +PhpGenerator 3.0 je kompatibilní s PHP 7.0 až 7.3 +PhpGenerator 2.6 je kompatibilní s PHP 5.6 až 7.3 + + +{{composer: nette/php-generator}} +{{leftbar: /utils/@left-menu}} diff --git a/docs/en/@home.texy b/docs/en/@home.texy new file mode 100644 index 00000000..11242fb5 --- /dev/null +++ b/docs/en/@home.texy @@ -0,0 +1,822 @@ +PHP Code Generator +****************** + +
+- Need to generate PHP code for classes, functions, PHP files, etc.? +- Supports all the latest PHP features like enums, attributes, etc. +- Allows you to easily modify existing classes +- PSR-12 compliant output +- Highly mature, stable, and widely used library +
+ + +Installation +------------ + +Download and install the package using [Composer|/best-practices/composer]: + +```shell +composer require nette/php-generator +``` + +For PHP compatibility, see the [table |#Compatibility Table]. + + +Classes +------- + +Let's start with a straightforward example of generating class using [ClassType |api:Nette\PhpGenerator\ClassType]: + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); + +$class + ->setFinal() + ->setExtends(ParentClass::class) + ->addImplement(Countable::class) + ->addComment("Description of class.\nSecond line\n") + ->addComment('@property-read Nette\Forms\Form $form'); + +// to generate PHP code simply cast to string or use echo: +echo $class; +``` + +It will render this result: + +```php +/** + * Description of class. + * Second line + * + * @property-read Nette\Forms\Form $form + */ +final class Demo extends ParentClass implements Countable +{ + use Nette\SmartObject; +} +``` + +We can also use a printer to generate the code, which, unlike `echo $class`, we will be able to [further configure |#Printers and PSR compliance]: + +```php +$printer = new Nette\PhpGenerator\Printer; +echo $printer->printClass($class); +``` + +We can add constants (class [Constant |api:Nette\PhpGenerator\Constant]) and properties (class [Property |api:Nette\PhpGenerator\Property]): + +```php +$class->addConstant('ID', 123) + ->setProtected() // constant visibility + ->setFinal(); + +$class->addProperty('items', [1, 2, 3]) + ->setPrivate() // or setVisibility('private') + ->setStatic() + ->addComment('@var int[]'); + +$class->addProperty('list') + ->setType('?array') + ->setInitialized(); // prints '= null' +``` + +It generates: + +```php +final protected const ID = 123; + +/** @var int[] */ +private static $items = [1, 2, 3]; + +public ?array $list = null; +``` + +And we can add [methods|#Method and Function Signature]: + +```php +$method = $class->addMethod('count') + ->addComment('Count it.') + ->setFinal() + ->setProtected() + ->setReturnType('?int') // method return type + ->setBody('return count($items ?: $this->items);'); + +$method->addParameter('items', []) // $items = [] + ->setReference() // &$items = [] + ->setType('array'); // array &$items = [] +``` + +It results in: + +```php +/** + * Count it. + */ +final protected function count(array &$items = []): ?int +{ + return count($items ?: $this->items); +} +``` + +Promoted parameters introduced by PHP 8.0 can be passed to the constructor (since v3.5): + +```php +$method = $class->addMethod('__construct'); +$method->addPromotedParameter('name'); +$method->addPromotedParameter('args', []) + ->setPrivate(); +``` + +It results in: + +```php +public function __construct( + public $name, + private $args = [], +) { +} +``` + +Readonly properties introduced by PHP 8.1 can be marked via `setReadOnly()`. + +------ + +If the added property, constant, method or parameter already exist, it throws exception. + +Members can be removed using `removeProperty()`, `removeConstant()`, `removeMethod()` or `removeParameter()`. + +You can also add existing `Method`, `Property` or `Constant` objects to the class: + +```php +$method = new Nette\PhpGenerator\Method('getHandle'); +$property = new Nette\PhpGenerator\Property('handle'); +$const = new Nette\PhpGenerator\Constant('ROLE'); + +$class = (new Nette\PhpGenerator\ClassType('Demo')) + ->addMember($method) + ->addMember($property) + ->addMember($const); +``` + +You can clone existing methods, properties and constants with a different name using `cloneWithName()`: + +```php +$methodCount = $class->getMethod('count'); +$methodRecount = $methodCount->cloneWithName('recount'); +$class->addMember($methodRecount); +``` + + +Interface or Trait +------------------ + +You can create interfaces and traits: + +```php +$interface = new Nette\PhpGenerator\InterfaceType('MyInterface'); +$trait = new Nette\PhpGenerator\TraitType('MyTrait'); +``` + +Using Traits: + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +$class->addTrait('SmartObject'); +$class->addTrait('MyTrait') + ->addResolution('sayHello as protected') + ->addComment('@use MyTrait'); +echo $class; +``` + +Result: + +```php +class Demo +{ + use SmartObject; + use MyTrait { + sayHello as protected; + } +} +``` + + +Enums .{data-version:v3.6} +-------------------------- + +You can easily create the enums that PHP 8.1 brings: + +```php +$enum = new Nette\PhpGenerator\EnumType('Suit'); +$enum->addCase('Clubs'); +$enum->addCase('Diamonds'); +$enum->addCase('Hearts'); +$enum->addCase('Spades'); + +echo $enum; +``` + +Result: + +```php +enum Suit +{ + case Clubs; + case Diamonds; + case Hearts; + case Spades; +} +``` + +You can also define scalar equivalents for cases to create a backed enum: + +```php +$enum->addCase('Clubs', '♣'); +$enum->addCase('Diamonds', '♦'); +``` + +It is possible to add a comment or [#attributes] to each case using `addComment()` or `addAttribute()`. + + +Anonymous Class +--------------- + +Give `null` as the name and you have an anonymous class: + +```php +$class = new Nette\PhpGenerator\ClassType(null); +$class->addMethod('__construct') + ->addParameter('foo'); + +echo '$obj = new class ($val) ' . $class . ';'; +``` + +Result: + +```php +$obj = new class ($val) { + + public function __construct($foo) + { + } +}; +``` + + +Global Function +--------------- + +Code of functions will generate class [GlobalFunction |api:Nette\PhpGenerator\GlobalFunction]: + +```php +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->setBody('return $a + $b;'); +$function->addParameter('a'); +$function->addParameter('b'); +echo $function; + +// or use PsrPrinter for output conforming to PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function); +``` + +Result: + +```php +function foo($a, $b) +{ + return $a + $b; +} +``` + + +Closure +------- + +Code of closures will generate class [Closure |api:Nette\PhpGenerator\Closure]: + +```php +$closure = new Nette\PhpGenerator\Closure; +$closure->setBody('return $a + $b;'); +$closure->addParameter('a'); +$closure->addParameter('b'); +$closure->addUse('c') + ->setReference(); +echo $closure; + +// or use PsrPrinter for output conforming to PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure); +``` + +Result: + +```php +function ($a, $b) use (&$c) { + return $a + $b; +} +``` + + +Arrow Function +-------------- + +You can also print closure as arrow function using printer: + +```php +$closure = new Nette\PhpGenerator\Closure; +$closure->setBody('$a + $b'); +$closure->addParameter('a'); +$closure->addParameter('b'); + +echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure); +``` + +Result: + +```php +fn($a, $b) => $a + $b +``` + + +Method and Function Signature +----------------------------- + +Methods are represented by the class [Method |api:Nette\PhpGenerator\Method]. You can set visibility, return value, add comments, [attributes|#Attributes] etc: + +```php +$method = $class->addMethod('count') + ->addComment('Count it.') + ->setFinal() + ->setProtected() + ->setReturnType('?int'); +``` + +Each parameter is represented by a class [Parameter |api:Nette\PhpGenerator\Parameter]. Again, you can set every conceivable property: + +```php +$method->addParameter('items', []) // $items = [] + ->setReference() // &$items = [] + ->setType('array'); // array &$items = [] + +// function count(&$items = []) +``` + +To define the so-called variadics parameters (or also the splat, spread, ellipsis, unpacking or three dots operator), use `setVariadics()`: + +```php +$method = $class->addMethod('count'); +$method->setVariadics(true); +$method->addParameter('items'); +``` + +Generates: + +```php +function count(...$items) +{ +} +``` + + +Method and Function Body +------------------------ + +The body can be passed to the `setBody()` method at once or sequentially (line by line) by repeatedly calling `addBody()`: + +```php +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addBody('$a = rand(10, 20);'); +$function->addBody('return $a;'); +echo $function; +``` + +Result + +```php +function foo() +{ + $a = rand(10, 20); + return $a; +} +``` + +You can use special placeholders for handy way to inject variables. + +Simple placeholders `?` + +```php +$str = 'any string'; +$num = 3; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addBody('return substr(?, ?);', [$str, $num]); +echo $function; +``` + +Result: + +```php +function foo() +{ + return substr('any string', 3); +} +``` + +Variadic placeholder `...?` + +```php +$items = [1, 2, 3]; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->setBody('myfunc(...?);', [$items]); +echo $function; +``` + +Result: + +```php +function foo() +{ + myfunc(1, 2, 3); +} +``` + +You can also use PHP 8 named parameters using placeholder `...?:` + +```php +$items = ['foo' => 1, 'bar' => true]; +$function->setBody('myfunc(...?:);', [$items]); + +// myfunc(foo: 1, bar: true); +``` + +Escape placeholder using slash `\?` + +```php +$num = 3; +$function = new Nette\PhpGenerator\GlobalFunction('foo'); +$function->addParameter('a'); +$function->addBody('return $a \? 10 : ?;', [$num]); +echo $function; +``` + +Result: + +```php +function foo($a) +{ + return $a ? 10 : 3; +} +``` + + +Printers and PSR Compliance +--------------------------- + +PHP code is generated by `Printer` objects. There is a `PsrPrinter` whose output conforms to PSR-2 and PSR-12 and uses spaces for indentation, and a `Printer` that uses tabs for indentation. + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +// ... + +$printer = new Nette\PhpGenerator\PsrPrinter; +echo $printer->printClass($class); // 4 spaces indentation +``` + +Need to customize printer behavior? Create your own by inheriting the `Printer` class. You can reconfigure these variables: + +```php +class MyPrinter extends Nette\PhpGenerator\Printer +{ + public int $wrapLength = 120; + public string $indentation = "\t"; + public int $linesBetweenProperties = 0; + public int $linesBetweenMethods = 2; + public int $linesBetweenUseTypes = 0; + public bool $bracesOnNextLine = true; + public string $returnTypeColon = ': '; +} +``` + + +Types +----- + +Each type or union/intersection type can be passed as a string, you can also use predefined constants for native types: + +```php +use Nette\PhpGenerator\Type; + +$member->setType('array'); // or Type::ARRAY; +$member->setType('array|string'); // or Type::union('array', 'string') +$member->setType('Foo&Bar'); // or Type::intersection(Foo::class, Bar::class) +$member->setType(null); // removes type +``` + +The same applies to the method `setReturnType()`. + + +Literals +-------- + +With `Literal` you can pass arbitrary PHP code to, for example, default property or parameter values etc: + +```php +use Nette\PhpGenerator\Literal; + +$class = new Nette\PhpGenerator\ClassType('Demo'); + +$class->addProperty('foo', new Literal('Iterator::SELF_FIRST')); + +$class->addMethod('bar') + ->addParameter('id', new Literal('1 + 2')); + +echo $class; +``` + +Result: + +```php +class Demo +{ + public $foo = Iterator::SELF_FIRST; + + public function bar($id = 1 + 2) + { + } +} +``` + +You can also pass parameters to `Literal` and have it formatted into valid PHP code using [special placeholders|#method-and-function-body-generator]: + +```php +new Literal('substr(?, ?)', [$a, $b]); +// generates, for example: substr('hello', 5); +``` + + +Attributes .{data-version:v3.5} +------------------------------- + +You can add PHP 8 attributes to all classes, methods, properties, constants, enum cases, functions, closures and parameters. [#Literals] can also be used as parameter values. + +```php +$class = new Nette\PhpGenerator\ClassType('Demo'); +$class->addAttribute('Deprecated'); + +$class->addProperty('list') + ->addAttribute('WithArguments', [1, 2]); + +$method = $class->addMethod('count') + ->addAttribute('Foo\Cached', ['mode' => true]); + +$method->addParameter('items') + ->addAttribute('Bar'); + +echo $class; +``` + +Result: + +```php +#[Deprecated] +class Demo +{ + #[WithArguments(1, 2)] + public $list; + + + #[Foo\Cached(mode: true)] + public function count(#[Bar] $items) + { + } +} +``` + + +Namespace +--------- + +Classes, traits, interfaces and enums (hereinafter classes) can be grouped into namespaces ([PhpNamespace |api:Nette\PhpGenerator\PhpNamespace]): + +```php +$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); + +// create new classes in the namespace +$class = $namespace->addClass('Task'); +$interface = $namespace->addInterface('Countable'); +$trait = $namespace->addTrait('NameAware'); + +// or insert an existing class into the namespace +$class = new Nette\PhpGenerator\ClassType('Task'); +$namespace->add($class); +``` + +If the class already exists, it throws exception. + +You can define use-statements: + +```php +// use Http\Request; +$namespace->addUse(Http\Request::class); +// use Http\Request as HttpReq; +$namespace->addUse(Http\Request::class, 'HttpReq'); +// use function iter\range; +$namespace->addUseFunction('iter\range'); +``` + +To simplify a fully qualified class, function or constant name according to the defined aliases, use the `simplifyName` method: + +```php +echo $namespace->simplifyName('Foo\Bar'); // 'Bar', because 'Foo' is current namespace +echo $namespace->simplifyName('iter\range', $namespace::NAME_FUNCTION); // 'range', because of the defined use-statement +``` + +Conversely, you can convert a simplified class, function or constant name to a fully qualified one using the `resolveName` method: + +```php +echo $namespace->resolveName('Bar'); // 'Foo\Bar' +echo $namespace->resolveName('range', $namespace::NAME_FUNCTION); // 'iter\range' +``` + + +Class Names Resolving +--------------------- + +**When the class is part of the namespace, it is rendered slightly differently**: all types (ie. type hints, return types, parent class name, +implemented interfaces, used traits and attributes) are automatically *resolved* (unless you turn it off, see below). +It means that you have to **use full class names** in definitions and they will be replaced +with aliases (according to the use-statements) or fully qualified names in the resulting code: + +```php +$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); +$namespace->addUse('Bar\AliasedClass'); + +$class = $namespace->addClass('Demo'); +$class->addImplement('Foo\A') // it will simplify to A + ->addTrait('Bar\AliasedClass'); // it will simplify to AliasedClass + +$method = $class->addMethod('method'); +$method->addComment('@return ' . $namespace->simplifyName('Foo\D')); // in comments simplify manually +$method->addParameter('arg') + ->setType('Bar\OtherClass'); // it will resolve to \Bar\OtherClass + +echo $namespace; + +// or use PsrPrinter for output conforming to PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace); +``` + +Result: + +```php +namespace Foo; + +use Bar\AliasedClass; + +class Demo implements A +{ + use AliasedClass; + + /** + * @return D + */ + public function method(\Bar\OtherClass $arg) + { + } +} +``` + +Auto-resolving can be turned off this way: + +```php +$printer = new Nette\PhpGenerator\Printer; // or PsrPrinter +$printer->setTypeResolving(false); +echo $printer->printNamespace($namespace); +``` + + +PHP Files +--------- + +Classes, functions and namespaces can be grouped into PHP files represented by the class [PhpFile|api:Nette\PhpGenerator\PhpFile]: + +```php +$file = new Nette\PhpGenerator\PhpFile; +$file->addComment('This file is auto-generated.'); +$file->setStrictTypes(); // adds declare(strict_types=1) + +$class = $file->addClass('Foo\A'); +$function = $file->addFunction('Foo\foo'); + +// or +// $namespace = $file->addNamespace('Foo'); +// $class = $namespace->addClass('A'); +// $function = $namespace->addFunction('foo'); + +echo $file; + +// or use PsrPrinter for output conforming to PSR-2 / PSR-12 +// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file); +``` + +Result: + +```php +dump($var); // prints ['a', 'b', 123] +``` + + +Compatibility Table +------------------- + +PhpGenerator 4.0 is compatible with PHP 8.0 to 8.2 +PhpGenerator 3.6 is compatible with PHP 7.2 to 8.2 +PhpGenerator 3.2 – 3.5 is compatible with PHP 7.1 to 8.0 +PhpGenerator 3.1 is compatible with PHP 7.1 to 7.3 +PhpGenerator 3.0 is compatible with PHP 7.0 to 7.3 +PhpGenerator 2.6 is compatible with PHP 5.6 to 7.3 + +{{composer: nette/php-generator}} +{{leftbar: /utils/@left-menu}}