new-admin-api/vendor/nette/php-generator/src/PhpGenerator/Printer.php

282 lines
8.3 KiB
PHP

<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\PhpGenerator;
use Nette;
use Nette\Utils\Strings;
/**
* Generates PHP code.
*/
class Printer
{
use Nette\SmartObject;
/** @var string */
protected $indentation = "\t";
/** @var int */
protected $linesBetweenMethods = 2;
/** @var bool */
private $resolveTypes = true;
public function printFunction(GlobalFunction $function, PhpNamespace $namespace = null): string
{
return Helpers::formatDocComment($function->getComment() . "\n")
. 'function '
. ($function->getReturnReference() ? '&' : '')
. $function->getName()
. $this->printParameters($function, $namespace)
. $this->printReturnType($function, $namespace)
. "\n{\n" . $this->indent(ltrim(rtrim($function->getBody()) . "\n")) . "}\n";
}
public function printClosure(Closure $closure): string
{
$uses = [];
foreach ($closure->getUses() as $param) {
$uses[] = ($param->isReference() ? '&' : '') . '$' . $param->getName();
}
$useStr = strlen($tmp = implode(', ', $uses)) > (new Dumper)->wrapLength && count($uses) > 1
? "\n" . $this->indentation . implode(",\n" . $this->indentation, $uses) . "\n"
: $tmp;
return 'function '
. ($closure->getReturnReference() ? '&' : '')
. $this->printParameters($closure, null)
. ($uses ? " use ($useStr)" : '')
. $this->printReturnType($closure, null)
. " {\n" . $this->indent(ltrim(rtrim($closure->getBody()) . "\n")) . '}';
}
public function printArrowFunction(Closure $closure): string
{
foreach ($closure->getUses() as $use) {
if ($use->isReference()) {
throw new Nette\InvalidArgumentException('Arrow function cannot bind variables by-reference.');
}
}
return 'fn '
. ($closure->getReturnReference() ? '&' : '')
. $this->printParameters($closure, null)
. $this->printReturnType($closure, null)
. ' => ' . trim($closure->getBody()) . ';';
}
public function printMethod(Method $method, PhpNamespace $namespace = null): string
{
$method->validate();
return Helpers::formatDocComment($method->getComment() . "\n")
. ($method->isAbstract() ? 'abstract ' : '')
. ($method->isFinal() ? 'final ' : '')
. ($method->getVisibility() ? $method->getVisibility() . ' ' : '')
. ($method->isStatic() ? 'static ' : '')
. 'function '
. ($method->getReturnReference() ? '&' : '')
. $method->getName()
. ($params = $this->printParameters($method, $namespace))
. $this->printReturnType($method, $namespace)
. ($method->isAbstract() || $method->getBody() === null
? ";\n"
: (strpos($params, "\n") === false ? "\n" : ' ')
. "{\n"
. $this->indent(ltrim(rtrim($method->getBody()) . "\n"))
. "}\n");
}
public function printClass(ClassType $class, PhpNamespace $namespace = null): string
{
$class->validate();
$resolver = $this->resolveTypes && $namespace ? [$namespace, 'unresolveName'] : function ($s) { return $s; };
$traits = [];
foreach ($class->getTraitResolutions() as $trait => $resolutions) {
$traits[] = 'use ' . $resolver($trait)
. ($resolutions ? " {\n" . $this->indentation . implode(";\n" . $this->indentation, $resolutions) . ";\n}\n" : ";\n");
}
$consts = [];
foreach ($class->getConstants() as $const) {
$def = ($const->getVisibility() ? $const->getVisibility() . ' ' : '') . 'const ' . $const->getName() . ' = ';
$consts[] = Helpers::formatDocComment((string) $const->getComment())
. $def
. $this->dump($const->getValue(), strlen($def)) . ";\n";
}
$properties = [];
foreach ($class->getProperties() as $property) {
$type = $property->getType();
$def = (($property->getVisibility() ?: 'public') . ($property->isStatic() ? ' static' : '') . ' '
. ltrim($this->printType($type, $property->isNullable(), $namespace) . ' ')
. '$' . $property->getName());
$properties[] = Helpers::formatDocComment((string) $property->getComment())
. $def
. ($property->getValue() === null && !$property->isInitialized() ? '' : ' = ' . $this->dump($property->getValue(), strlen($def) + 3)) // 3 = ' = '
. ";\n";
}
$methods = [];
foreach ($class->getMethods() as $method) {
$methods[] = $this->printMethod($method, $namespace);
}
$members = array_filter([
implode('', $traits),
implode('', $consts),
implode("\n", $properties),
($methods && $properties ? str_repeat("\n", $this->linesBetweenMethods - 1) : '')
. implode(str_repeat("\n", $this->linesBetweenMethods), $methods),
]);
return Strings::normalize(
Helpers::formatDocComment($class->getComment() . "\n")
. ($class->isAbstract() ? 'abstract ' : '')
. ($class->isFinal() ? 'final ' : '')
. ($class->getName() ? $class->getType() . ' ' . $class->getName() . ' ' : '')
. ($class->getExtends() ? 'extends ' . implode(', ', array_map($resolver, (array) $class->getExtends())) . ' ' : '')
. ($class->getImplements() ? 'implements ' . implode(', ', array_map($resolver, $class->getImplements())) . ' ' : '')
. ($class->getName() ? "\n" : '') . "{\n"
. ($members ? $this->indent(implode("\n", $members)) : '')
. '}'
) . ($class->getName() ? "\n" : '');
}
public function printNamespace(PhpNamespace $namespace): string
{
$name = $namespace->getName();
$uses = $this->printUses($namespace);
$classes = [];
foreach ($namespace->getClasses() as $class) {
$classes[] = $this->printClass($class, $namespace);
}
$body = ($uses ? $uses . "\n\n" : '')
. implode("\n", $classes);
if ($namespace->hasBracketedSyntax()) {
return 'namespace' . ($name ? " $name" : '') . "\n{\n"
. $this->indent($body)
. "}\n";
} else {
return ($name ? "namespace $name;\n\n" : '')
. $body;
}
}
public function printFile(PhpFile $file): string
{
$namespaces = [];
foreach ($file->getNamespaces() as $namespace) {
$namespaces[] = $this->printNamespace($namespace);
}
return Strings::normalize(
"<?php\n"
. ($file->getComment() ? "\n" . Helpers::formatDocComment($file->getComment() . "\n") : '')
. "\n"
. ($file->hasStrictTypes() ? "declare(strict_types=1);\n\n" : '')
. implode("\n\n", $namespaces)
) . "\n";
}
/** @return static */
public function setTypeResolving(bool $state = true): self
{
$this->resolveTypes = $state;
return $this;
}
protected function indent(string $s): string
{
$s = str_replace("\t", $this->indentation, $s);
return Strings::indent($s, 1, $this->indentation);
}
protected function dump($var, int $column = 0): string
{
return (new Dumper)->dump($var, $column);
}
protected function printUses(PhpNamespace $namespace): string
{
$name = $namespace->getName();
$uses = [];
foreach ($namespace->getUses() as $alias => $original) {
if ($original !== ($name ? $name . '\\' . $alias : $alias)) {
if ($alias === $original || substr($original, -(strlen($alias) + 1)) === '\\' . $alias) {
$uses[] = "use $original;";
} else {
$uses[] = "use $original as $alias;";
}
}
}
return implode("\n", $uses);
}
/**
* @param Closure|GlobalFunction|Method $function
*/
public function printParameters($function, PhpNamespace $namespace = null): string
{
$params = [];
$list = $function->getParameters();
foreach ($list as $param) {
$variadic = $function->isVariadic() && $param === end($list);
$type = $param->getType();
$params[] = ltrim($this->printType($type, $param->isNullable(), $namespace) . ' ')
. ($param->isReference() ? '&' : '')
. ($variadic ? '...' : '')
. '$' . $param->getName()
. ($param->hasDefaultValue() && !$variadic ? ' = ' . $this->dump($param->getDefaultValue()) : '');
}
return strlen($tmp = implode(', ', $params)) > (new Dumper)->wrapLength && count($params) > 1
? "(\n" . $this->indentation . implode(",\n" . $this->indentation, $params) . "\n)"
: "($tmp)";
}
public function printType(?string $type, bool $nullable = false, PhpNamespace $namespace = null): string
{
return $type
? ($nullable ? '?' : '') . ($this->resolveTypes && $namespace ? $namespace->unresolveName($type) : $type)
: '';
}
/**
* @param Closure|GlobalFunction|Method $function
*/
private function printReturnType($function, ?PhpNamespace $namespace): string
{
return ($tmp = $this->printType($function->getReturnType(), $function->isReturnNullable(), $namespace))
? ': ' . $tmp
: '';
}
}