Completed
Push — master ( 8e973a...811a95 )
by
unknown
03:40 queued 01:04
created
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php 1 patch
Indentation   +146 added lines, -146 removed lines patch added patch discarded remove patch
@@ -12,150 +12,150 @@
 block discarded – undo
12 12
 
13 13
 class Property implements PhpParser\Builder
14 14
 {
15
-    protected $name;
16
-
17
-    protected $flags = 0;
18
-    protected $default = null;
19
-    protected $attributes = [];
20
-
21
-    /** @var null|Identifier|Name|NullableType */
22
-    protected $type;
23
-
24
-    /** @var Node\AttributeGroup[] */
25
-    protected $attributeGroups = [];
26
-
27
-    /**
28
-     * Creates a property builder.
29
-     *
30
-     * @param string $name Name of the property
31
-     */
32
-    public function __construct(string $name) {
33
-        $this->name = $name;
34
-    }
35
-
36
-    /**
37
-     * Makes the property public.
38
-     *
39
-     * @return $this The builder instance (for fluid interface)
40
-     */
41
-    public function makePublic() {
42
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
43
-
44
-        return $this;
45
-    }
46
-
47
-    /**
48
-     * Makes the property protected.
49
-     *
50
-     * @return $this The builder instance (for fluid interface)
51
-     */
52
-    public function makeProtected() {
53
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
54
-
55
-        return $this;
56
-    }
57
-
58
-    /**
59
-     * Makes the property private.
60
-     *
61
-     * @return $this The builder instance (for fluid interface)
62
-     */
63
-    public function makePrivate() {
64
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
65
-
66
-        return $this;
67
-    }
68
-
69
-    /**
70
-     * Makes the property static.
71
-     *
72
-     * @return $this The builder instance (for fluid interface)
73
-     */
74
-    public function makeStatic() {
75
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
76
-
77
-        return $this;
78
-    }
79
-
80
-    /**
81
-     * Makes the property readonly.
82
-     *
83
-     * @return $this The builder instance (for fluid interface)
84
-     */
85
-    public function makeReadonly() {
86
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
87
-
88
-        return $this;
89
-    }
90
-
91
-    /**
92
-     * Sets default value for the property.
93
-     *
94
-     * @param mixed $value Default value to use
95
-     *
96
-     * @return $this The builder instance (for fluid interface)
97
-     */
98
-    public function setDefault($value) {
99
-        $this->default = BuilderHelpers::normalizeValue($value);
100
-
101
-        return $this;
102
-    }
103
-
104
-    /**
105
-     * Sets doc comment for the property.
106
-     *
107
-     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
108
-     *
109
-     * @return $this The builder instance (for fluid interface)
110
-     */
111
-    public function setDocComment($docComment) {
112
-        $this->attributes = [
113
-            'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
114
-        ];
115
-
116
-        return $this;
117
-    }
118
-
119
-    /**
120
-     * Sets the property type for PHP 7.4+.
121
-     *
122
-     * @param string|Name|Identifier|ComplexType $type
123
-     *
124
-     * @return $this
125
-     */
126
-    public function setType($type) {
127
-        $this->type = BuilderHelpers::normalizeType($type);
128
-
129
-        return $this;
130
-    }
131
-
132
-    /**
133
-     * Adds an attribute group.
134
-     *
135
-     * @param Node\Attribute|Node\AttributeGroup $attribute
136
-     *
137
-     * @return $this The builder instance (for fluid interface)
138
-     */
139
-    public function addAttribute($attribute) {
140
-        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
141
-
142
-        return $this;
143
-    }
144
-
145
-    /**
146
-     * Returns the built class node.
147
-     *
148
-     * @return Stmt\Property The built property node
149
-     */
150
-    public function getNode() : PhpParser\Node {
151
-        return new Stmt\Property(
152
-            $this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC,
153
-            [
154
-                new Stmt\PropertyProperty($this->name, $this->default)
155
-            ],
156
-            $this->attributes,
157
-            $this->type,
158
-            $this->attributeGroups
159
-        );
160
-    }
15
+	protected $name;
16
+
17
+	protected $flags = 0;
18
+	protected $default = null;
19
+	protected $attributes = [];
20
+
21
+	/** @var null|Identifier|Name|NullableType */
22
+	protected $type;
23
+
24
+	/** @var Node\AttributeGroup[] */
25
+	protected $attributeGroups = [];
26
+
27
+	/**
28
+	 * Creates a property builder.
29
+	 *
30
+	 * @param string $name Name of the property
31
+	 */
32
+	public function __construct(string $name) {
33
+		$this->name = $name;
34
+	}
35
+
36
+	/**
37
+	 * Makes the property public.
38
+	 *
39
+	 * @return $this The builder instance (for fluid interface)
40
+	 */
41
+	public function makePublic() {
42
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
43
+
44
+		return $this;
45
+	}
46
+
47
+	/**
48
+	 * Makes the property protected.
49
+	 *
50
+	 * @return $this The builder instance (for fluid interface)
51
+	 */
52
+	public function makeProtected() {
53
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
54
+
55
+		return $this;
56
+	}
57
+
58
+	/**
59
+	 * Makes the property private.
60
+	 *
61
+	 * @return $this The builder instance (for fluid interface)
62
+	 */
63
+	public function makePrivate() {
64
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
65
+
66
+		return $this;
67
+	}
68
+
69
+	/**
70
+	 * Makes the property static.
71
+	 *
72
+	 * @return $this The builder instance (for fluid interface)
73
+	 */
74
+	public function makeStatic() {
75
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
76
+
77
+		return $this;
78
+	}
79
+
80
+	/**
81
+	 * Makes the property readonly.
82
+	 *
83
+	 * @return $this The builder instance (for fluid interface)
84
+	 */
85
+	public function makeReadonly() {
86
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
87
+
88
+		return $this;
89
+	}
90
+
91
+	/**
92
+	 * Sets default value for the property.
93
+	 *
94
+	 * @param mixed $value Default value to use
95
+	 *
96
+	 * @return $this The builder instance (for fluid interface)
97
+	 */
98
+	public function setDefault($value) {
99
+		$this->default = BuilderHelpers::normalizeValue($value);
100
+
101
+		return $this;
102
+	}
103
+
104
+	/**
105
+	 * Sets doc comment for the property.
106
+	 *
107
+	 * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
108
+	 *
109
+	 * @return $this The builder instance (for fluid interface)
110
+	 */
111
+	public function setDocComment($docComment) {
112
+		$this->attributes = [
113
+			'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
114
+		];
115
+
116
+		return $this;
117
+	}
118
+
119
+	/**
120
+	 * Sets the property type for PHP 7.4+.
121
+	 *
122
+	 * @param string|Name|Identifier|ComplexType $type
123
+	 *
124
+	 * @return $this
125
+	 */
126
+	public function setType($type) {
127
+		$this->type = BuilderHelpers::normalizeType($type);
128
+
129
+		return $this;
130
+	}
131
+
132
+	/**
133
+	 * Adds an attribute group.
134
+	 *
135
+	 * @param Node\Attribute|Node\AttributeGroup $attribute
136
+	 *
137
+	 * @return $this The builder instance (for fluid interface)
138
+	 */
139
+	public function addAttribute($attribute) {
140
+		$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
141
+
142
+		return $this;
143
+	}
144
+
145
+	/**
146
+	 * Returns the built class node.
147
+	 *
148
+	 * @return Stmt\Property The built property node
149
+	 */
150
+	public function getNode() : PhpParser\Node {
151
+		return new Stmt\Property(
152
+			$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC,
153
+			[
154
+				new Stmt\PropertyProperty($this->name, $this->default)
155
+			],
156
+			$this->attributes,
157
+			$this->type,
158
+			$this->attributeGroups
159
+		);
160
+	}
161 161
 }
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php 2 patches
Indentation   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -9,70 +9,70 @@
 block discarded – undo
9 9
 
10 10
 class Trait_ extends Declaration
11 11
 {
12
-    protected $name;
13
-    protected $uses = [];
14
-    protected $properties = [];
15
-    protected $methods = [];
12
+	protected $name;
13
+	protected $uses = [];
14
+	protected $properties = [];
15
+	protected $methods = [];
16 16
 
17
-    /** @var Node\AttributeGroup[] */
18
-    protected $attributeGroups = [];
17
+	/** @var Node\AttributeGroup[] */
18
+	protected $attributeGroups = [];
19 19
 
20
-    /**
21
-     * Creates an interface builder.
22
-     *
23
-     * @param string $name Name of the interface
24
-     */
25
-    public function __construct(string $name) {
26
-        $this->name = $name;
27
-    }
20
+	/**
21
+	 * Creates an interface builder.
22
+	 *
23
+	 * @param string $name Name of the interface
24
+	 */
25
+	public function __construct(string $name) {
26
+		$this->name = $name;
27
+	}
28 28
 
29
-    /**
30
-     * Adds a statement.
31
-     *
32
-     * @param Stmt|PhpParser\Builder $stmt The statement to add
33
-     *
34
-     * @return $this The builder instance (for fluid interface)
35
-     */
36
-    public function addStmt($stmt) {
37
-        $stmt = BuilderHelpers::normalizeNode($stmt);
29
+	/**
30
+	 * Adds a statement.
31
+	 *
32
+	 * @param Stmt|PhpParser\Builder $stmt The statement to add
33
+	 *
34
+	 * @return $this The builder instance (for fluid interface)
35
+	 */
36
+	public function addStmt($stmt) {
37
+		$stmt = BuilderHelpers::normalizeNode($stmt);
38 38
 
39
-        if ($stmt instanceof Stmt\Property) {
40
-            $this->properties[] = $stmt;
41
-        } elseif ($stmt instanceof Stmt\ClassMethod) {
42
-            $this->methods[] = $stmt;
43
-        } elseif ($stmt instanceof Stmt\TraitUse) {
44
-            $this->uses[] = $stmt;
45
-        } else {
46
-            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
47
-        }
39
+		if ($stmt instanceof Stmt\Property) {
40
+			$this->properties[] = $stmt;
41
+		} elseif ($stmt instanceof Stmt\ClassMethod) {
42
+			$this->methods[] = $stmt;
43
+		} elseif ($stmt instanceof Stmt\TraitUse) {
44
+			$this->uses[] = $stmt;
45
+		} else {
46
+			throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
47
+		}
48 48
 
49
-        return $this;
50
-    }
49
+		return $this;
50
+	}
51 51
 
52
-    /**
53
-     * Adds an attribute group.
54
-     *
55
-     * @param Node\Attribute|Node\AttributeGroup $attribute
56
-     *
57
-     * @return $this The builder instance (for fluid interface)
58
-     */
59
-    public function addAttribute($attribute) {
60
-        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
52
+	/**
53
+	 * Adds an attribute group.
54
+	 *
55
+	 * @param Node\Attribute|Node\AttributeGroup $attribute
56
+	 *
57
+	 * @return $this The builder instance (for fluid interface)
58
+	 */
59
+	public function addAttribute($attribute) {
60
+		$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
61 61
 
62
-        return $this;
63
-    }
62
+		return $this;
63
+	}
64 64
 
65
-    /**
66
-     * Returns the built trait node.
67
-     *
68
-     * @return Stmt\Trait_ The built interface node
69
-     */
70
-    public function getNode() : PhpParser\Node {
71
-        return new Stmt\Trait_(
72
-            $this->name, [
73
-                'stmts' => array_merge($this->uses, $this->properties, $this->methods),
74
-                'attrGroups' => $this->attributeGroups,
75
-            ], $this->attributes
76
-        );
77
-    }
65
+	/**
66
+	 * Returns the built trait node.
67
+	 *
68
+	 * @return Stmt\Trait_ The built interface node
69
+	 */
70
+	public function getNode() : PhpParser\Node {
71
+		return new Stmt\Trait_(
72
+			$this->name, [
73
+				'stmts' => array_merge($this->uses, $this->properties, $this->methods),
74
+				'attrGroups' => $this->attributeGroups,
75
+			], $this->attributes
76
+		);
77
+	}
78 78
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -7,8 +7,7 @@
 block discarded – undo
7 7
 use PhpParser\Node;
8 8
 use PhpParser\Node\Stmt;
9 9
 
10
-class Trait_ extends Declaration
11
-{
10
+class Trait_ extends Declaration {
12 11
     protected $name;
13 12
     protected $uses = [];
14 13
     protected $properties = [];
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php 2 patches
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -9,37 +9,37 @@
 block discarded – undo
9 9
 
10 10
 class Namespace_ extends Declaration
11 11
 {
12
-    private $name;
13
-    private $stmts = [];
12
+	private $name;
13
+	private $stmts = [];
14 14
 
15
-    /**
16
-     * Creates a namespace builder.
17
-     *
18
-     * @param Node\Name|string|null $name Name of the namespace
19
-     */
20
-    public function __construct($name) {
21
-        $this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
22
-    }
15
+	/**
16
+	 * Creates a namespace builder.
17
+	 *
18
+	 * @param Node\Name|string|null $name Name of the namespace
19
+	 */
20
+	public function __construct($name) {
21
+		$this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
22
+	}
23 23
 
24
-    /**
25
-     * Adds a statement.
26
-     *
27
-     * @param Node|PhpParser\Builder $stmt The statement to add
28
-     *
29
-     * @return $this The builder instance (for fluid interface)
30
-     */
31
-    public function addStmt($stmt) {
32
-        $this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
24
+	/**
25
+	 * Adds a statement.
26
+	 *
27
+	 * @param Node|PhpParser\Builder $stmt The statement to add
28
+	 *
29
+	 * @return $this The builder instance (for fluid interface)
30
+	 */
31
+	public function addStmt($stmt) {
32
+		$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
33 33
 
34
-        return $this;
35
-    }
34
+		return $this;
35
+	}
36 36
 
37
-    /**
38
-     * Returns the built node.
39
-     *
40
-     * @return Stmt\Namespace_ The built node
41
-     */
42
-    public function getNode() : Node {
43
-        return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
44
-    }
37
+	/**
38
+	 * Returns the built node.
39
+	 *
40
+	 * @return Stmt\Namespace_ The built node
41
+	 */
42
+	public function getNode() : Node {
43
+		return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
44
+	}
45 45
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -7,8 +7,7 @@
 block discarded – undo
7 7
 use PhpParser\Node;
8 8
 use PhpParser\Node\Stmt;
9 9
 
10
-class Namespace_ extends Declaration
11
-{
10
+class Namespace_ extends Declaration {
12 11
     private $name;
13 12
     private $stmts = [];
14 13
 
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php 1 patch
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -13,136 +13,136 @@
 block discarded – undo
13 13
 
14 14
 class ClassConst implements PhpParser\Builder
15 15
 {
16
-    protected $flags = 0;
17
-    protected $attributes = [];
18
-    protected $constants = [];
19
-
20
-    /** @var Node\AttributeGroup[] */
21
-    protected $attributeGroups = [];
22
-    /** @var Identifier|Node\Name|Node\ComplexType */
23
-    protected $type;
24
-
25
-    /**
26
-     * Creates a class constant builder
27
-     *
28
-     * @param string|Identifier                          $name  Name
29
-     * @param Node\Expr|bool|null|int|float|string|array $value Value
30
-     */
31
-    public function __construct($name, $value) {
32
-        $this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
33
-    }
34
-
35
-    /**
36
-     * Add another constant to const group
37
-     *
38
-     * @param string|Identifier                          $name  Name
39
-     * @param Node\Expr|bool|null|int|float|string|array $value Value
40
-     *
41
-     * @return $this The builder instance (for fluid interface)
42
-     */
43
-    public function addConst($name, $value) {
44
-        $this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
45
-
46
-        return $this;
47
-    }
48
-
49
-    /**
50
-     * Makes the constant public.
51
-     *
52
-     * @return $this The builder instance (for fluid interface)
53
-     */
54
-    public function makePublic() {
55
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
56
-
57
-        return $this;
58
-    }
59
-
60
-    /**
61
-     * Makes the constant protected.
62
-     *
63
-     * @return $this The builder instance (for fluid interface)
64
-     */
65
-    public function makeProtected() {
66
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
67
-
68
-        return $this;
69
-    }
70
-
71
-    /**
72
-     * Makes the constant private.
73
-     *
74
-     * @return $this The builder instance (for fluid interface)
75
-     */
76
-    public function makePrivate() {
77
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
78
-
79
-        return $this;
80
-    }
81
-
82
-    /**
83
-     * Makes the constant final.
84
-     *
85
-     * @return $this The builder instance (for fluid interface)
86
-     */
87
-    public function makeFinal() {
88
-        $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
89
-
90
-        return $this;
91
-    }
92
-
93
-    /**
94
-     * Sets doc comment for the constant.
95
-     *
96
-     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
97
-     *
98
-     * @return $this The builder instance (for fluid interface)
99
-     */
100
-    public function setDocComment($docComment) {
101
-        $this->attributes = [
102
-            'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
103
-        ];
104
-
105
-        return $this;
106
-    }
107
-
108
-    /**
109
-     * Adds an attribute group.
110
-     *
111
-     * @param Node\Attribute|Node\AttributeGroup $attribute
112
-     *
113
-     * @return $this The builder instance (for fluid interface)
114
-     */
115
-    public function addAttribute($attribute) {
116
-        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
117
-
118
-        return $this;
119
-    }
120
-
121
-    /**
122
-     * Sets the constant type.
123
-     *
124
-     * @param string|Node\Name|Identifier|Node\ComplexType $type
125
-     *
126
-     * @return $this
127
-     */
128
-    public function setType($type) {
129
-        $this->type = BuilderHelpers::normalizeType($type);
130
-
131
-        return $this;
132
-    }
133
-
134
-    /**
135
-     * Returns the built class node.
136
-     *
137
-     * @return Stmt\ClassConst The built constant node
138
-     */
139
-    public function getNode(): PhpParser\Node {
140
-        return new Stmt\ClassConst(
141
-            $this->constants,
142
-            $this->flags,
143
-            $this->attributes,
144
-            $this->attributeGroups,
145
-            $this->type
146
-        );
147
-    }
16
+	protected $flags = 0;
17
+	protected $attributes = [];
18
+	protected $constants = [];
19
+
20
+	/** @var Node\AttributeGroup[] */
21
+	protected $attributeGroups = [];
22
+	/** @var Identifier|Node\Name|Node\ComplexType */
23
+	protected $type;
24
+
25
+	/**
26
+	 * Creates a class constant builder
27
+	 *
28
+	 * @param string|Identifier                          $name  Name
29
+	 * @param Node\Expr|bool|null|int|float|string|array $value Value
30
+	 */
31
+	public function __construct($name, $value) {
32
+		$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
33
+	}
34
+
35
+	/**
36
+	 * Add another constant to const group
37
+	 *
38
+	 * @param string|Identifier                          $name  Name
39
+	 * @param Node\Expr|bool|null|int|float|string|array $value Value
40
+	 *
41
+	 * @return $this The builder instance (for fluid interface)
42
+	 */
43
+	public function addConst($name, $value) {
44
+		$this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
45
+
46
+		return $this;
47
+	}
48
+
49
+	/**
50
+	 * Makes the constant public.
51
+	 *
52
+	 * @return $this The builder instance (for fluid interface)
53
+	 */
54
+	public function makePublic() {
55
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
56
+
57
+		return $this;
58
+	}
59
+
60
+	/**
61
+	 * Makes the constant protected.
62
+	 *
63
+	 * @return $this The builder instance (for fluid interface)
64
+	 */
65
+	public function makeProtected() {
66
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
67
+
68
+		return $this;
69
+	}
70
+
71
+	/**
72
+	 * Makes the constant private.
73
+	 *
74
+	 * @return $this The builder instance (for fluid interface)
75
+	 */
76
+	public function makePrivate() {
77
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
78
+
79
+		return $this;
80
+	}
81
+
82
+	/**
83
+	 * Makes the constant final.
84
+	 *
85
+	 * @return $this The builder instance (for fluid interface)
86
+	 */
87
+	public function makeFinal() {
88
+		$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
89
+
90
+		return $this;
91
+	}
92
+
93
+	/**
94
+	 * Sets doc comment for the constant.
95
+	 *
96
+	 * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
97
+	 *
98
+	 * @return $this The builder instance (for fluid interface)
99
+	 */
100
+	public function setDocComment($docComment) {
101
+		$this->attributes = [
102
+			'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
103
+		];
104
+
105
+		return $this;
106
+	}
107
+
108
+	/**
109
+	 * Adds an attribute group.
110
+	 *
111
+	 * @param Node\Attribute|Node\AttributeGroup $attribute
112
+	 *
113
+	 * @return $this The builder instance (for fluid interface)
114
+	 */
115
+	public function addAttribute($attribute) {
116
+		$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
117
+
118
+		return $this;
119
+	}
120
+
121
+	/**
122
+	 * Sets the constant type.
123
+	 *
124
+	 * @param string|Node\Name|Identifier|Node\ComplexType $type
125
+	 *
126
+	 * @return $this
127
+	 */
128
+	public function setType($type) {
129
+		$this->type = BuilderHelpers::normalizeType($type);
130
+
131
+		return $this;
132
+	}
133
+
134
+	/**
135
+	 * Returns the built class node.
136
+	 *
137
+	 * @return Stmt\ClassConst The built constant node
138
+	 */
139
+	public function getNode(): PhpParser\Node {
140
+		return new Stmt\ClassConst(
141
+			$this->constants,
142
+			$this->flags,
143
+			$this->attributes,
144
+			$this->attributeGroups,
145
+			$this->type
146
+		);
147
+	}
148 148
 }
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php 2 patches
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -9,41 +9,41 @@
 block discarded – undo
9 9
 
10 10
 class Use_ implements Builder
11 11
 {
12
-    protected $name;
13
-    protected $type;
14
-    protected $alias = null;
12
+	protected $name;
13
+	protected $type;
14
+	protected $alias = null;
15 15
 
16
-    /**
17
-     * Creates a name use (alias) builder.
18
-     *
19
-     * @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
20
-     * @param int              $type One of the Stmt\Use_::TYPE_* constants
21
-     */
22
-    public function __construct($name, int $type) {
23
-        $this->name = BuilderHelpers::normalizeName($name);
24
-        $this->type = $type;
25
-    }
16
+	/**
17
+	 * Creates a name use (alias) builder.
18
+	 *
19
+	 * @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
20
+	 * @param int              $type One of the Stmt\Use_::TYPE_* constants
21
+	 */
22
+	public function __construct($name, int $type) {
23
+		$this->name = BuilderHelpers::normalizeName($name);
24
+		$this->type = $type;
25
+	}
26 26
 
27
-    /**
28
-     * Sets alias for used name.
29
-     *
30
-     * @param string $alias Alias to use (last component of full name by default)
31
-     *
32
-     * @return $this The builder instance (for fluid interface)
33
-     */
34
-    public function as(string $alias) {
35
-        $this->alias = $alias;
36
-        return $this;
37
-    }
27
+	/**
28
+	 * Sets alias for used name.
29
+	 *
30
+	 * @param string $alias Alias to use (last component of full name by default)
31
+	 *
32
+	 * @return $this The builder instance (for fluid interface)
33
+	 */
34
+	public function as(string $alias) {
35
+		$this->alias = $alias;
36
+		return $this;
37
+	}
38 38
 
39
-    /**
40
-     * Returns the built node.
41
-     *
42
-     * @return Stmt\Use_ The built node
43
-     */
44
-    public function getNode() : Node {
45
-        return new Stmt\Use_([
46
-            new Stmt\UseUse($this->name, $this->alias)
47
-        ], $this->type);
48
-    }
39
+	/**
40
+	 * Returns the built node.
41
+	 *
42
+	 * @return Stmt\Use_ The built node
43
+	 */
44
+	public function getNode() : Node {
45
+		return new Stmt\Use_([
46
+			new Stmt\UseUse($this->name, $this->alias)
47
+		], $this->type);
48
+	}
49 49
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -7,8 +7,7 @@
 block discarded – undo
7 7
 use PhpParser\Node;
8 8
 use PhpParser\Node\Stmt;
9 9
 
10
-class Use_ implements Builder
11
-{
10
+class Use_ implements Builder {
12 11
     protected $name;
13 12
     protected $type;
14 13
     protected $alias = null;
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php 2 patches
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -9,56 +9,56 @@
 block discarded – undo
9 9
 
10 10
 class TraitUse implements Builder
11 11
 {
12
-    protected $traits = [];
13
-    protected $adaptations = [];
12
+	protected $traits = [];
13
+	protected $adaptations = [];
14 14
 
15
-    /**
16
-     * Creates a trait use builder.
17
-     *
18
-     * @param Node\Name|string ...$traits Names of used traits
19
-     */
20
-    public function __construct(...$traits) {
21
-        foreach ($traits as $trait) {
22
-            $this->and($trait);
23
-        }
24
-    }
15
+	/**
16
+	 * Creates a trait use builder.
17
+	 *
18
+	 * @param Node\Name|string ...$traits Names of used traits
19
+	 */
20
+	public function __construct(...$traits) {
21
+		foreach ($traits as $trait) {
22
+			$this->and($trait);
23
+		}
24
+	}
25 25
 
26
-    /**
27
-     * Adds used trait.
28
-     *
29
-     * @param Node\Name|string $trait Trait name
30
-     *
31
-     * @return $this The builder instance (for fluid interface)
32
-     */
33
-    public function and($trait) {
34
-        $this->traits[] = BuilderHelpers::normalizeName($trait);
35
-        return $this;
36
-    }
26
+	/**
27
+	 * Adds used trait.
28
+	 *
29
+	 * @param Node\Name|string $trait Trait name
30
+	 *
31
+	 * @return $this The builder instance (for fluid interface)
32
+	 */
33
+	public function and($trait) {
34
+		$this->traits[] = BuilderHelpers::normalizeName($trait);
35
+		return $this;
36
+	}
37 37
 
38
-    /**
39
-     * Adds trait adaptation.
40
-     *
41
-     * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation
42
-     *
43
-     * @return $this The builder instance (for fluid interface)
44
-     */
45
-    public function with($adaptation) {
46
-        $adaptation = BuilderHelpers::normalizeNode($adaptation);
38
+	/**
39
+	 * Adds trait adaptation.
40
+	 *
41
+	 * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation
42
+	 *
43
+	 * @return $this The builder instance (for fluid interface)
44
+	 */
45
+	public function with($adaptation) {
46
+		$adaptation = BuilderHelpers::normalizeNode($adaptation);
47 47
 
48
-        if (!$adaptation instanceof Stmt\TraitUseAdaptation) {
49
-            throw new \LogicException('Adaptation must have type TraitUseAdaptation');
50
-        }
48
+		if (!$adaptation instanceof Stmt\TraitUseAdaptation) {
49
+			throw new \LogicException('Adaptation must have type TraitUseAdaptation');
50
+		}
51 51
 
52
-        $this->adaptations[] = $adaptation;
53
-        return $this;
54
-    }
52
+		$this->adaptations[] = $adaptation;
53
+		return $this;
54
+	}
55 55
 
56
-    /**
57
-     * Returns the built node.
58
-     *
59
-     * @return Node The built node
60
-     */
61
-    public function getNode() : Node {
62
-        return new Stmt\TraitUse($this->traits, $this->adaptations);
63
-    }
56
+	/**
57
+	 * Returns the built node.
58
+	 *
59
+	 * @return Node The built node
60
+	 */
61
+	public function getNode() : Node {
62
+		return new Stmt\TraitUse($this->traits, $this->adaptations);
63
+	}
64 64
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -7,8 +7,7 @@
 block discarded – undo
7 7
 use PhpParser\Node;
8 8
 use PhpParser\Node\Stmt;
9 9
 
10
-class TraitUse implements Builder
11
-{
10
+class TraitUse implements Builder {
12 11
     protected $traits = [];
13 12
     protected $adaptations = [];
14 13
 
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php 2 patches
Indentation   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -12,7 +12,7 @@
 block discarded – undo
12 12
  */
13 13
 class Throwing implements ErrorHandler
14 14
 {
15
-    public function handleError(Error $error) {
16
-        throw $error;
17
-    }
15
+	public function handleError(Error $error) {
16
+		throw $error;
17
+	}
18 18
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -10,8 +10,7 @@
 block discarded – undo
10 10
  *
11 11
  * This is the default strategy used by all components.
12 12
  */
13
-class Throwing implements ErrorHandler
14
-{
13
+class Throwing implements ErrorHandler {
15 14
     public function handleError(Error $error) {
16 15
         throw $error;
17 16
     }
Please login to merge, or discard this patch.
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php 2 patches
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -12,35 +12,35 @@
 block discarded – undo
12 12
  */
13 13
 class Collecting implements ErrorHandler
14 14
 {
15
-    /** @var Error[] Collected errors */
16
-    private $errors = [];
15
+	/** @var Error[] Collected errors */
16
+	private $errors = [];
17 17
 
18
-    public function handleError(Error $error) {
19
-        $this->errors[] = $error;
20
-    }
18
+	public function handleError(Error $error) {
19
+		$this->errors[] = $error;
20
+	}
21 21
 
22
-    /**
23
-     * Get collected errors.
24
-     *
25
-     * @return Error[]
26
-     */
27
-    public function getErrors() : array {
28
-        return $this->errors;
29
-    }
22
+	/**
23
+	 * Get collected errors.
24
+	 *
25
+	 * @return Error[]
26
+	 */
27
+	public function getErrors() : array {
28
+		return $this->errors;
29
+	}
30 30
 
31
-    /**
32
-     * Check whether there are any errors.
33
-     *
34
-     * @return bool
35
-     */
36
-    public function hasErrors() : bool {
37
-        return !empty($this->errors);
38
-    }
31
+	/**
32
+	 * Check whether there are any errors.
33
+	 *
34
+	 * @return bool
35
+	 */
36
+	public function hasErrors() : bool {
37
+		return !empty($this->errors);
38
+	}
39 39
 
40
-    /**
41
-     * Reset/clear collected errors.
42
-     */
43
-    public function clearErrors() {
44
-        $this->errors = [];
45
-    }
40
+	/**
41
+	 * Reset/clear collected errors.
42
+	 */
43
+	public function clearErrors() {
44
+		$this->errors = [];
45
+	}
46 46
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -10,8 +10,7 @@
 block discarded – undo
10 10
  *
11 11
  * This allows graceful handling of errors.
12 12
  */
13
-class Collecting implements ErrorHandler
14
-{
13
+class Collecting implements ErrorHandler {
15 14
     /** @var Error[] Collected errors */
16 15
     private $errors = [];
17 16
 
Please login to merge, or discard this patch.
php-scoper/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php 3 patches
Indentation   +1534 added lines, -1534 removed lines patch added patch discarded remove patch
@@ -14,1517 +14,1517 @@  discard block
 block discarded – undo
14 14
 
15 15
 abstract class PrettyPrinterAbstract
16 16
 {
17
-    const FIXUP_PREC_LEFT       = 0; // LHS operand affected by precedence
18
-    const FIXUP_PREC_RIGHT      = 1; // RHS operand affected by precedence
19
-    const FIXUP_CALL_LHS        = 2; // LHS of call
20
-    const FIXUP_DEREF_LHS       = 3; // LHS of dereferencing operation
21
-    const FIXUP_BRACED_NAME     = 4; // Name operand that may require bracing
22
-    const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
23
-    const FIXUP_ENCAPSED        = 6; // Encapsed string part
24
-    const FIXUP_NEW             = 7; // New/instanceof operand
25
-    const FIXUP_STATIC_DEREF_LHS = 8; // LHS of static dereferencing operation
26
-
27
-    protected $precedenceMap = [
28
-        // [precedence, associativity]
29
-        // where for precedence -1 is %left, 0 is %nonassoc and 1 is %right
30
-        BinaryOp\Pow::class            => [  0,  1],
31
-        Expr\BitwiseNot::class         => [ 10,  1],
32
-        Expr\PreInc::class             => [ 10,  1],
33
-        Expr\PreDec::class             => [ 10,  1],
34
-        Expr\PostInc::class            => [ 10, -1],
35
-        Expr\PostDec::class            => [ 10, -1],
36
-        Expr\UnaryPlus::class          => [ 10,  1],
37
-        Expr\UnaryMinus::class         => [ 10,  1],
38
-        Cast\Int_::class               => [ 10,  1],
39
-        Cast\Double::class             => [ 10,  1],
40
-        Cast\String_::class            => [ 10,  1],
41
-        Cast\Array_::class             => [ 10,  1],
42
-        Cast\Object_::class            => [ 10,  1],
43
-        Cast\Bool_::class              => [ 10,  1],
44
-        Cast\Unset_::class             => [ 10,  1],
45
-        Expr\ErrorSuppress::class      => [ 10,  1],
46
-        Expr\Instanceof_::class        => [ 20,  0],
47
-        Expr\BooleanNot::class         => [ 30,  1],
48
-        BinaryOp\Mul::class            => [ 40, -1],
49
-        BinaryOp\Div::class            => [ 40, -1],
50
-        BinaryOp\Mod::class            => [ 40, -1],
51
-        BinaryOp\Plus::class           => [ 50, -1],
52
-        BinaryOp\Minus::class          => [ 50, -1],
53
-        BinaryOp\Concat::class         => [ 50, -1],
54
-        BinaryOp\ShiftLeft::class      => [ 60, -1],
55
-        BinaryOp\ShiftRight::class     => [ 60, -1],
56
-        BinaryOp\Smaller::class        => [ 70,  0],
57
-        BinaryOp\SmallerOrEqual::class => [ 70,  0],
58
-        BinaryOp\Greater::class        => [ 70,  0],
59
-        BinaryOp\GreaterOrEqual::class => [ 70,  0],
60
-        BinaryOp\Equal::class          => [ 80,  0],
61
-        BinaryOp\NotEqual::class       => [ 80,  0],
62
-        BinaryOp\Identical::class      => [ 80,  0],
63
-        BinaryOp\NotIdentical::class   => [ 80,  0],
64
-        BinaryOp\Spaceship::class      => [ 80,  0],
65
-        BinaryOp\BitwiseAnd::class     => [ 90, -1],
66
-        BinaryOp\BitwiseXor::class     => [100, -1],
67
-        BinaryOp\BitwiseOr::class      => [110, -1],
68
-        BinaryOp\BooleanAnd::class     => [120, -1],
69
-        BinaryOp\BooleanOr::class      => [130, -1],
70
-        BinaryOp\Coalesce::class       => [140,  1],
71
-        Expr\Ternary::class            => [150,  0],
72
-        // parser uses %left for assignments, but they really behave as %right
73
-        Expr\Assign::class             => [160,  1],
74
-        Expr\AssignRef::class          => [160,  1],
75
-        AssignOp\Plus::class           => [160,  1],
76
-        AssignOp\Minus::class          => [160,  1],
77
-        AssignOp\Mul::class            => [160,  1],
78
-        AssignOp\Div::class            => [160,  1],
79
-        AssignOp\Concat::class         => [160,  1],
80
-        AssignOp\Mod::class            => [160,  1],
81
-        AssignOp\BitwiseAnd::class     => [160,  1],
82
-        AssignOp\BitwiseOr::class      => [160,  1],
83
-        AssignOp\BitwiseXor::class     => [160,  1],
84
-        AssignOp\ShiftLeft::class      => [160,  1],
85
-        AssignOp\ShiftRight::class     => [160,  1],
86
-        AssignOp\Pow::class            => [160,  1],
87
-        AssignOp\Coalesce::class       => [160,  1],
88
-        Expr\YieldFrom::class          => [165,  1],
89
-        Expr\Print_::class             => [168,  1],
90
-        BinaryOp\LogicalAnd::class     => [170, -1],
91
-        BinaryOp\LogicalXor::class     => [180, -1],
92
-        BinaryOp\LogicalOr::class      => [190, -1],
93
-        Expr\Include_::class           => [200, -1],
94
-    ];
95
-
96
-    /** @var int Current indentation level. */
97
-    protected $indentLevel;
98
-    /** @var string Newline including current indentation. */
99
-    protected $nl;
100
-    /** @var string Token placed at end of doc string to ensure it is followed by a newline. */
101
-    protected $docStringEndToken;
102
-    /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
103
-    protected $canUseSemicolonNamespaces;
104
-    /** @var array Pretty printer options */
105
-    protected $options;
106
-
107
-    /** @var TokenStream Original tokens for use in format-preserving pretty print */
108
-    protected $origTokens;
109
-    /** @var Internal\Differ Differ for node lists */
110
-    protected $nodeListDiffer;
111
-    /** @var bool[] Map determining whether a certain character is a label character */
112
-    protected $labelCharMap;
113
-    /**
114
-     * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used
115
-     *              during format-preserving prints to place additional parens/braces if necessary.
116
-     */
117
-    protected $fixupMap;
118
-    /**
119
-     * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r],
120
-     *              where $l and $r specify the token type that needs to be stripped when removing
121
-     *              this node.
122
-     */
123
-    protected $removalMap;
124
-    /**
125
-     * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight].
126
-     *              $find is an optional token after which the insertion occurs. $extraLeft/Right
127
-     *              are optionally added before/after the main insertions.
128
-     */
129
-    protected $insertionMap;
130
-    /**
131
-     * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted
132
-     *               between elements of this list subnode.
133
-     */
134
-    protected $listInsertionMap;
135
-    protected $emptyListInsertionMap;
136
-    /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers
137
-     *             should be reprinted. */
138
-    protected $modifierChangeMap;
139
-
140
-    /**
141
-     * Creates a pretty printer instance using the given options.
142
-     *
143
-     * Supported options:
144
-     *  * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array
145
-     *                                    syntax, if the node does not specify a format.
146
-     *
147
-     * @param array $options Dictionary of formatting options
148
-     */
149
-    public function __construct(array $options = []) {
150
-        $this->docStringEndToken = '_DOC_STRING_END_' . mt_rand();
151
-
152
-        $defaultOptions = ['shortArraySyntax' => false];
153
-        $this->options = $options + $defaultOptions;
154
-    }
155
-
156
-    /**
157
-     * Reset pretty printing state.
158
-     */
159
-    protected function resetState() {
160
-        $this->indentLevel = 0;
161
-        $this->nl = "\n";
162
-        $this->origTokens = null;
163
-    }
164
-
165
-    /**
166
-     * Set indentation level
167
-     *
168
-     * @param int $level Level in number of spaces
169
-     */
170
-    protected function setIndentLevel(int $level) {
171
-        $this->indentLevel = $level;
172
-        $this->nl = "\n" . \str_repeat(' ', $level);
173
-    }
174
-
175
-    /**
176
-     * Increase indentation level.
177
-     */
178
-    protected function indent() {
179
-        $this->indentLevel += 4;
180
-        $this->nl .= '    ';
181
-    }
182
-
183
-    /**
184
-     * Decrease indentation level.
185
-     */
186
-    protected function outdent() {
187
-        assert($this->indentLevel >= 4);
188
-        $this->indentLevel -= 4;
189
-        $this->nl = "\n" . str_repeat(' ', $this->indentLevel);
190
-    }
191
-
192
-    /**
193
-     * Pretty prints an array of statements.
194
-     *
195
-     * @param Node[] $stmts Array of statements
196
-     *
197
-     * @return string Pretty printed statements
198
-     */
199
-    public function prettyPrint(array $stmts) : string {
200
-        $this->resetState();
201
-        $this->preprocessNodes($stmts);
202
-
203
-        return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
204
-    }
205
-
206
-    /**
207
-     * Pretty prints an expression.
208
-     *
209
-     * @param Expr $node Expression node
210
-     *
211
-     * @return string Pretty printed node
212
-     */
213
-    public function prettyPrintExpr(Expr $node) : string {
214
-        $this->resetState();
215
-        return $this->handleMagicTokens($this->p($node));
216
-    }
217
-
218
-    /**
219
-     * Pretty prints a file of statements (includes the opening <?php tag if it is required).
220
-     *
221
-     * @param Node[] $stmts Array of statements
222
-     *
223
-     * @return string Pretty printed statements
224
-     */
225
-    public function prettyPrintFile(array $stmts) : string {
226
-        if (!$stmts) {
227
-            return "<?php\n\n";
228
-        }
229
-
230
-        $p = "<?php\n\n" . $this->prettyPrint($stmts);
231
-
232
-        if ($stmts[0] instanceof Stmt\InlineHTML) {
233
-            $p = preg_replace('/^<\?php\s+\?>\n?/', '', $p);
234
-        }
235
-        if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
236
-            $p = preg_replace('/<\?php$/', '', rtrim($p));
237
-        }
238
-
239
-        return $p;
240
-    }
241
-
242
-    /**
243
-     * Preprocesses the top-level nodes to initialize pretty printer state.
244
-     *
245
-     * @param Node[] $nodes Array of nodes
246
-     */
247
-    protected function preprocessNodes(array $nodes) {
248
-        /* We can use semicolon-namespaces unless there is a global namespace declaration */
249
-        $this->canUseSemicolonNamespaces = true;
250
-        foreach ($nodes as $node) {
251
-            if ($node instanceof Stmt\Namespace_ && null === $node->name) {
252
-                $this->canUseSemicolonNamespaces = false;
253
-                break;
254
-            }
255
-        }
256
-    }
257
-
258
-    /**
259
-     * Handles (and removes) no-indent and doc-string-end tokens.
260
-     *
261
-     * @param string $str
262
-     * @return string
263
-     */
264
-    protected function handleMagicTokens(string $str) : string {
265
-        // Replace doc-string-end tokens with nothing or a newline
266
-        $str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
267
-        $str = str_replace($this->docStringEndToken, "\n", $str);
268
-
269
-        return $str;
270
-    }
271
-
272
-    /**
273
-     * Pretty prints an array of nodes (statements) and indents them optionally.
274
-     *
275
-     * @param Node[] $nodes  Array of nodes
276
-     * @param bool   $indent Whether to indent the printed nodes
277
-     *
278
-     * @return string Pretty printed statements
279
-     */
280
-    protected function pStmts(array $nodes, bool $indent = true) : string {
281
-        if ($indent) {
282
-            $this->indent();
283
-        }
284
-
285
-        $result = '';
286
-        foreach ($nodes as $node) {
287
-            $comments = $node->getComments();
288
-            if ($comments) {
289
-                $result .= $this->nl . $this->pComments($comments);
290
-                if ($node instanceof Stmt\Nop) {
291
-                    continue;
292
-                }
293
-            }
294
-
295
-            $result .= $this->nl . $this->p($node);
296
-        }
297
-
298
-        if ($indent) {
299
-            $this->outdent();
300
-        }
301
-
302
-        return $result;
303
-    }
304
-
305
-    /**
306
-     * Pretty-print an infix operation while taking precedence into account.
307
-     *
308
-     * @param string $class          Node class of operator
309
-     * @param Node   $leftNode       Left-hand side node
310
-     * @param string $operatorString String representation of the operator
311
-     * @param Node   $rightNode      Right-hand side node
312
-     *
313
-     * @return string Pretty printed infix operation
314
-     */
315
-    protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string {
316
-        list($precedence, $associativity) = $this->precedenceMap[$class];
317
-
318
-        return $this->pPrec($leftNode, $precedence, $associativity, -1)
319
-             . $operatorString
320
-             . $this->pPrec($rightNode, $precedence, $associativity, 1);
321
-    }
322
-
323
-    /**
324
-     * Pretty-print a prefix operation while taking precedence into account.
325
-     *
326
-     * @param string $class          Node class of operator
327
-     * @param string $operatorString String representation of the operator
328
-     * @param Node   $node           Node
329
-     *
330
-     * @return string Pretty printed prefix operation
331
-     */
332
-    protected function pPrefixOp(string $class, string $operatorString, Node $node) : string {
333
-        list($precedence, $associativity) = $this->precedenceMap[$class];
334
-        return $operatorString . $this->pPrec($node, $precedence, $associativity, 1);
335
-    }
336
-
337
-    /**
338
-     * Pretty-print a postfix operation while taking precedence into account.
339
-     *
340
-     * @param string $class          Node class of operator
341
-     * @param string $operatorString String representation of the operator
342
-     * @param Node   $node           Node
343
-     *
344
-     * @return string Pretty printed postfix operation
345
-     */
346
-    protected function pPostfixOp(string $class, Node $node, string $operatorString) : string {
347
-        list($precedence, $associativity) = $this->precedenceMap[$class];
348
-        return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString;
349
-    }
350
-
351
-    /**
352
-     * Prints an expression node with the least amount of parentheses necessary to preserve the meaning.
353
-     *
354
-     * @param Node $node                Node to pretty print
355
-     * @param int  $parentPrecedence    Precedence of the parent operator
356
-     * @param int  $parentAssociativity Associativity of parent operator
357
-     *                                  (-1 is left, 0 is nonassoc, 1 is right)
358
-     * @param int  $childPosition       Position of the node relative to the operator
359
-     *                                  (-1 is left, 1 is right)
360
-     *
361
-     * @return string The pretty printed node
362
-     */
363
-    protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string {
364
-        $class = \get_class($node);
365
-        if (isset($this->precedenceMap[$class])) {
366
-            $childPrecedence = $this->precedenceMap[$class][0];
367
-            if ($childPrecedence > $parentPrecedence
368
-                || ($parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition)
369
-            ) {
370
-                return '(' . $this->p($node) . ')';
371
-            }
372
-        }
373
-
374
-        return $this->p($node);
375
-    }
376
-
377
-    /**
378
-     * Pretty prints an array of nodes and implodes the printed values.
379
-     *
380
-     * @param Node[] $nodes Array of Nodes to be printed
381
-     * @param string $glue  Character to implode with
382
-     *
383
-     * @return string Imploded pretty printed nodes
384
-     */
385
-    protected function pImplode(array $nodes, string $glue = '') : string {
386
-        $pNodes = [];
387
-        foreach ($nodes as $node) {
388
-            if (null === $node) {
389
-                $pNodes[] = '';
390
-            } else {
391
-                $pNodes[] = $this->p($node);
392
-            }
393
-        }
394
-
395
-        return implode($glue, $pNodes);
396
-    }
397
-
398
-    /**
399
-     * Pretty prints an array of nodes and implodes the printed values with commas.
400
-     *
401
-     * @param Node[] $nodes Array of Nodes to be printed
402
-     *
403
-     * @return string Comma separated pretty printed nodes
404
-     */
405
-    protected function pCommaSeparated(array $nodes) : string {
406
-        return $this->pImplode($nodes, ', ');
407
-    }
408
-
409
-    /**
410
-     * Pretty prints a comma-separated list of nodes in multiline style, including comments.
411
-     *
412
-     * The result includes a leading newline and one level of indentation (same as pStmts).
413
-     *
414
-     * @param Node[] $nodes         Array of Nodes to be printed
415
-     * @param bool   $trailingComma Whether to use a trailing comma
416
-     *
417
-     * @return string Comma separated pretty printed nodes in multiline style
418
-     */
419
-    protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string {
420
-        $this->indent();
421
-
422
-        $result = '';
423
-        $lastIdx = count($nodes) - 1;
424
-        foreach ($nodes as $idx => $node) {
425
-            if ($node !== null) {
426
-                $comments = $node->getComments();
427
-                if ($comments) {
428
-                    $result .= $this->nl . $this->pComments($comments);
429
-                }
430
-
431
-                $result .= $this->nl . $this->p($node);
432
-            } else {
433
-                $result .= $this->nl;
434
-            }
435
-            if ($trailingComma || $idx !== $lastIdx) {
436
-                $result .= ',';
437
-            }
438
-        }
439
-
440
-        $this->outdent();
441
-        return $result;
442
-    }
443
-
444
-    /**
445
-     * Prints reformatted text of the passed comments.
446
-     *
447
-     * @param Comment[] $comments List of comments
448
-     *
449
-     * @return string Reformatted text of comments
450
-     */
451
-    protected function pComments(array $comments) : string {
452
-        $formattedComments = [];
453
-
454
-        foreach ($comments as $comment) {
455
-            $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
456
-        }
457
-
458
-        return implode($this->nl, $formattedComments);
459
-    }
460
-
461
-    /**
462
-     * Perform a format-preserving pretty print of an AST.
463
-     *
464
-     * The format preservation is best effort. For some changes to the AST the formatting will not
465
-     * be preserved (at least not locally).
466
-     *
467
-     * In order to use this method a number of prerequisites must be satisfied:
468
-     *  * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
469
-     *  * The CloningVisitor must be run on the AST prior to modification.
470
-     *  * The original tokens must be provided, using the getTokens() method on the lexer.
471
-     *
472
-     * @param Node[] $stmts      Modified AST with links to original AST
473
-     * @param Node[] $origStmts  Original AST with token offset information
474
-     * @param array  $origTokens Tokens of the original code
475
-     *
476
-     * @return string
477
-     */
478
-    public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string {
479
-        $this->initializeNodeListDiffer();
480
-        $this->initializeLabelCharMap();
481
-        $this->initializeFixupMap();
482
-        $this->initializeRemovalMap();
483
-        $this->initializeInsertionMap();
484
-        $this->initializeListInsertionMap();
485
-        $this->initializeEmptyListInsertionMap();
486
-        $this->initializeModifierChangeMap();
487
-
488
-        $this->resetState();
489
-        $this->origTokens = new TokenStream($origTokens);
490
-
491
-        $this->preprocessNodes($stmts);
492
-
493
-        $pos = 0;
494
-        $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null);
495
-        if (null !== $result) {
496
-            $result .= $this->origTokens->getTokenCode($pos, count($origTokens), 0);
497
-        } else {
498
-            // Fallback
499
-            // TODO Add <?php properly
500
-            $result = "<?php\n" . $this->pStmts($stmts, false);
501
-        }
502
-
503
-        return ltrim($this->handleMagicTokens($result));
504
-    }
505
-
506
-    protected function pFallback(Node $node) {
507
-        return $this->{'p' . $node->getType()}($node);
508
-    }
509
-
510
-    /**
511
-     * Pretty prints a node.
512
-     *
513
-     * This method also handles formatting preservation for nodes.
514
-     *
515
-     * @param Node $node Node to be pretty printed
516
-     * @param bool $parentFormatPreserved Whether parent node has preserved formatting
517
-     *
518
-     * @return string Pretty printed node
519
-     */
520
-    protected function p(Node $node, $parentFormatPreserved = false) : string {
521
-        // No orig tokens means this is a normal pretty print without preservation of formatting
522
-        if (!$this->origTokens) {
523
-            return $this->{'p' . $node->getType()}($node);
524
-        }
525
-
526
-        /** @var Node $origNode */
527
-        $origNode = $node->getAttribute('origNode');
528
-        if (null === $origNode) {
529
-            return $this->pFallback($node);
530
-        }
531
-
532
-        $class = \get_class($node);
533
-        \assert($class === \get_class($origNode));
534
-
535
-        $startPos = $origNode->getStartTokenPos();
536
-        $endPos = $origNode->getEndTokenPos();
537
-        \assert($startPos >= 0 && $endPos >= 0);
538
-
539
-        $fallbackNode = $node;
540
-        if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) {
541
-            // Normalize node structure of anonymous classes
542
-            $node = PrintableNewAnonClassNode::fromNewNode($node);
543
-            $origNode = PrintableNewAnonClassNode::fromNewNode($origNode);
544
-        }
545
-
546
-        // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting
547
-        // is not preserved, then we need to use the fallback code to make sure the tags are
548
-        // printed.
549
-        if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) {
550
-            return $this->pFallback($fallbackNode);
551
-        }
552
-
553
-        $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos);
554
-
555
-        $type = $node->getType();
556
-        $fixupInfo = $this->fixupMap[$class] ?? null;
557
-
558
-        $result = '';
559
-        $pos = $startPos;
560
-        foreach ($node->getSubNodeNames() as $subNodeName) {
561
-            $subNode = $node->$subNodeName;
562
-            $origSubNode = $origNode->$subNodeName;
563
-
564
-            if ((!$subNode instanceof Node && $subNode !== null)
565
-                || (!$origSubNode instanceof Node && $origSubNode !== null)
566
-            ) {
567
-                if ($subNode === $origSubNode) {
568
-                    // Unchanged, can reuse old code
569
-                    continue;
570
-                }
571
-
572
-                if (is_array($subNode) && is_array($origSubNode)) {
573
-                    // Array subnode changed, we might be able to reconstruct it
574
-                    $listResult = $this->pArray(
575
-                        $subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName,
576
-                        $fixupInfo[$subNodeName] ?? null
577
-                    );
578
-                    if (null === $listResult) {
579
-                        return $this->pFallback($fallbackNode);
580
-                    }
581
-
582
-                    $result .= $listResult;
583
-                    continue;
584
-                }
585
-
586
-                if (is_int($subNode) && is_int($origSubNode)) {
587
-                    // Check if this is a modifier change
588
-                    $key = $type . '->' . $subNodeName;
589
-                    if (!isset($this->modifierChangeMap[$key])) {
590
-                        return $this->pFallback($fallbackNode);
591
-                    }
592
-
593
-                    $findToken = $this->modifierChangeMap[$key];
594
-                    $result .= $this->pModifiers($subNode);
595
-                    $pos = $this->origTokens->findRight($pos, $findToken);
596
-                    continue;
597
-                }
598
-
599
-                // If a non-node, non-array subnode changed, we don't be able to do a partial
600
-                // reconstructions, as we don't have enough offset information. Pretty print the
601
-                // whole node instead.
602
-                return $this->pFallback($fallbackNode);
603
-            }
604
-
605
-            $extraLeft = '';
606
-            $extraRight = '';
607
-            if ($origSubNode !== null) {
608
-                $subStartPos = $origSubNode->getStartTokenPos();
609
-                $subEndPos = $origSubNode->getEndTokenPos();
610
-                \assert($subStartPos >= 0 && $subEndPos >= 0);
611
-            } else {
612
-                if ($subNode === null) {
613
-                    // Both null, nothing to do
614
-                    continue;
615
-                }
616
-
617
-                // A node has been inserted, check if we have insertion information for it
618
-                $key = $type . '->' . $subNodeName;
619
-                if (!isset($this->insertionMap[$key])) {
620
-                    return $this->pFallback($fallbackNode);
621
-                }
622
-
623
-                list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
624
-                if (null !== $findToken) {
625
-                    $subStartPos = $this->origTokens->findRight($pos, $findToken)
626
-                        + (int) !$beforeToken;
627
-                } else {
628
-                    $subStartPos = $pos;
629
-                }
630
-
631
-                if (null === $extraLeft && null !== $extraRight) {
632
-                    // If inserting on the right only, skipping whitespace looks better
633
-                    $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos);
634
-                }
635
-                $subEndPos = $subStartPos - 1;
636
-            }
637
-
638
-            if (null === $subNode) {
639
-                // A node has been removed, check if we have removal information for it
640
-                $key = $type . '->' . $subNodeName;
641
-                if (!isset($this->removalMap[$key])) {
642
-                    return $this->pFallback($fallbackNode);
643
-                }
644
-
645
-                // Adjust positions to account for additional tokens that must be skipped
646
-                $removalInfo = $this->removalMap[$key];
647
-                if (isset($removalInfo['left'])) {
648
-                    $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1;
649
-                }
650
-                if (isset($removalInfo['right'])) {
651
-                    $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1;
652
-                }
653
-            }
654
-
655
-            $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment);
656
-
657
-            if (null !== $subNode) {
658
-                $result .= $extraLeft;
659
-
660
-                $origIndentLevel = $this->indentLevel;
661
-                $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
662
-
663
-                // If it's the same node that was previously in this position, it certainly doesn't
664
-                // need fixup. It's important to check this here, because our fixup checks are more
665
-                // conservative than strictly necessary.
666
-                if (isset($fixupInfo[$subNodeName])
667
-                    && $subNode->getAttribute('origNode') !== $origSubNode
668
-                ) {
669
-                    $fixup = $fixupInfo[$subNodeName];
670
-                    $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos);
671
-                } else {
672
-                    $res = $this->p($subNode, true);
673
-                }
674
-
675
-                $this->safeAppend($result, $res);
676
-                $this->setIndentLevel($origIndentLevel);
677
-
678
-                $result .= $extraRight;
679
-            }
680
-
681
-            $pos = $subEndPos + 1;
682
-        }
683
-
684
-        $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment);
685
-        return $result;
686
-    }
687
-
688
-    /**
689
-     * Perform a format-preserving pretty print of an array.
690
-     *
691
-     * @param array       $nodes            New nodes
692
-     * @param array       $origNodes        Original nodes
693
-     * @param int         $pos              Current token position (updated by reference)
694
-     * @param int         $indentAdjustment Adjustment for indentation
695
-     * @param string      $parentNodeType   Type of the containing node.
696
-     * @param string      $subNodeName      Name of array subnode.
697
-     * @param null|int    $fixup            Fixup information for array item nodes
698
-     *
699
-     * @return null|string Result of pretty print or null if cannot preserve formatting
700
-     */
701
-    protected function pArray(
702
-        array $nodes, array $origNodes, int &$pos, int $indentAdjustment,
703
-        string $parentNodeType, string $subNodeName, $fixup
704
-    ) {
705
-        $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);
706
-
707
-        $mapKey = $parentNodeType . '->' . $subNodeName;
708
-        $insertStr = $this->listInsertionMap[$mapKey] ?? null;
709
-        $isStmtList = $subNodeName === 'stmts';
710
-
711
-        $beforeFirstKeepOrReplace = true;
712
-        $skipRemovedNode = false;
713
-        $delayedAdd = [];
714
-        $lastElemIndentLevel = $this->indentLevel;
715
-
716
-        $insertNewline = false;
717
-        if ($insertStr === "\n") {
718
-            $insertStr = '';
719
-            $insertNewline = true;
720
-        }
721
-
722
-        if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
723
-            $startPos = $origNodes[0]->getStartTokenPos();
724
-            $endPos = $origNodes[0]->getEndTokenPos();
725
-            \assert($startPos >= 0 && $endPos >= 0);
726
-            if (!$this->origTokens->haveBraces($startPos, $endPos)) {
727
-                // This was a single statement without braces, but either additional statements
728
-                // have been added, or the single statement has been removed. This requires the
729
-                // addition of braces. For now fall back.
730
-                // TODO: Try to preserve formatting
731
-                return null;
732
-            }
733
-        }
734
-
735
-        $result = '';
736
-        foreach ($diff as $i => $diffElem) {
737
-            $diffType = $diffElem->type;
738
-            /** @var Node|null $arrItem */
739
-            $arrItem = $diffElem->new;
740
-            /** @var Node|null $origArrItem */
741
-            $origArrItem = $diffElem->old;
742
-
743
-            if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
744
-                $beforeFirstKeepOrReplace = false;
745
-
746
-                if ($origArrItem === null || $arrItem === null) {
747
-                    // We can only handle the case where both are null
748
-                    if ($origArrItem === $arrItem) {
749
-                        continue;
750
-                    }
751
-                    return null;
752
-                }
753
-
754
-                if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
755
-                    // We can only deal with nodes. This can occur for Names, which use string arrays.
756
-                    return null;
757
-                }
758
-
759
-                $itemStartPos = $origArrItem->getStartTokenPos();
760
-                $itemEndPos = $origArrItem->getEndTokenPos();
761
-                \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
762
-
763
-                $origIndentLevel = $this->indentLevel;
764
-                $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
765
-                $this->setIndentLevel($lastElemIndentLevel);
766
-
767
-                $comments = $arrItem->getComments();
768
-                $origComments = $origArrItem->getComments();
769
-                $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
770
-                \assert($commentStartPos >= 0);
771
-
772
-                if ($commentStartPos < $pos) {
773
-                    // Comments may be assigned to multiple nodes if they start at the same position.
774
-                    // Make sure we don't try to print them multiple times.
775
-                    $commentStartPos = $itemStartPos;
776
-                }
777
-
778
-                if ($skipRemovedNode) {
779
-                    if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
780
-                                        $this->origTokens->haveTagInRange($pos, $itemStartPos))) {
781
-                        // We'd remove the brace of a code block.
782
-                        // TODO: Preserve formatting.
783
-                        $this->setIndentLevel($origIndentLevel);
784
-                        return null;
785
-                    }
786
-                } else {
787
-                    $result .= $this->origTokens->getTokenCode(
788
-                        $pos, $commentStartPos, $indentAdjustment);
789
-                }
790
-
791
-                if (!empty($delayedAdd)) {
792
-                    /** @var Node $delayedAddNode */
793
-                    foreach ($delayedAdd as $delayedAddNode) {
794
-                        if ($insertNewline) {
795
-                            $delayedAddComments = $delayedAddNode->getComments();
796
-                            if ($delayedAddComments) {
797
-                                $result .= $this->pComments($delayedAddComments) . $this->nl;
798
-                            }
799
-                        }
800
-
801
-                        $this->safeAppend($result, $this->p($delayedAddNode, true));
802
-
803
-                        if ($insertNewline) {
804
-                            $result .= $insertStr . $this->nl;
805
-                        } else {
806
-                            $result .= $insertStr;
807
-                        }
808
-                    }
809
-
810
-                    $delayedAdd = [];
811
-                }
812
-
813
-                if ($comments !== $origComments) {
814
-                    if ($comments) {
815
-                        $result .= $this->pComments($comments) . $this->nl;
816
-                    }
817
-                } else {
818
-                    $result .= $this->origTokens->getTokenCode(
819
-                        $commentStartPos, $itemStartPos, $indentAdjustment);
820
-                }
821
-
822
-                // If we had to remove anything, we have done so now.
823
-                $skipRemovedNode = false;
824
-            } elseif ($diffType === DiffElem::TYPE_ADD) {
825
-                if (null === $insertStr) {
826
-                    // We don't have insertion information for this list type
827
-                    return null;
828
-                }
829
-
830
-                // We go multiline if the original code was multiline,
831
-                // or if it's an array item with a comment above it.
832
-                if ($insertStr === ', ' &&
833
-                    ($this->isMultiline($origNodes) || $arrItem->getComments())
834
-                ) {
835
-                    $insertStr = ',';
836
-                    $insertNewline = true;
837
-                }
838
-
839
-                if ($beforeFirstKeepOrReplace) {
840
-                    // Will be inserted at the next "replace" or "keep" element
841
-                    $delayedAdd[] = $arrItem;
842
-                    continue;
843
-                }
844
-
845
-                $itemStartPos = $pos;
846
-                $itemEndPos = $pos - 1;
847
-
848
-                $origIndentLevel = $this->indentLevel;
849
-                $this->setIndentLevel($lastElemIndentLevel);
850
-
851
-                if ($insertNewline) {
852
-                    $result .= $insertStr . $this->nl;
853
-                    $comments = $arrItem->getComments();
854
-                    if ($comments) {
855
-                        $result .= $this->pComments($comments) . $this->nl;
856
-                    }
857
-                } else {
858
-                    $result .= $insertStr;
859
-                }
860
-            } elseif ($diffType === DiffElem::TYPE_REMOVE) {
861
-                if (!$origArrItem instanceof Node) {
862
-                    // We only support removal for nodes
863
-                    return null;
864
-                }
865
-
866
-                $itemStartPos = $origArrItem->getStartTokenPos();
867
-                $itemEndPos = $origArrItem->getEndTokenPos();
868
-                \assert($itemStartPos >= 0 && $itemEndPos >= 0);
869
-
870
-                // Consider comments part of the node.
871
-                $origComments = $origArrItem->getComments();
872
-                if ($origComments) {
873
-                    $itemStartPos = $origComments[0]->getStartTokenPos();
874
-                }
875
-
876
-                if ($i === 0) {
877
-                    // If we're removing from the start, keep the tokens before the node and drop those after it,
878
-                    // instead of the other way around.
879
-                    $result .= $this->origTokens->getTokenCode(
880
-                        $pos, $itemStartPos, $indentAdjustment);
881
-                    $skipRemovedNode = true;
882
-                } else {
883
-                    if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
884
-                                        $this->origTokens->haveTagInRange($pos, $itemStartPos))) {
885
-                        // We'd remove the brace of a code block.
886
-                        // TODO: Preserve formatting.
887
-                        return null;
888
-                    }
889
-                }
890
-
891
-                $pos = $itemEndPos + 1;
892
-                continue;
893
-            } else {
894
-                throw new \Exception("Shouldn't happen");
895
-            }
896
-
897
-            if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
898
-                $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
899
-            } else {
900
-                $res = $this->p($arrItem, true);
901
-            }
902
-            $this->safeAppend($result, $res);
903
-
904
-            $this->setIndentLevel($origIndentLevel);
905
-            $pos = $itemEndPos + 1;
906
-        }
907
-
908
-        if ($skipRemovedNode) {
909
-            // TODO: Support removing single node.
910
-            return null;
911
-        }
912
-
913
-        if (!empty($delayedAdd)) {
914
-            if (!isset($this->emptyListInsertionMap[$mapKey])) {
915
-                return null;
916
-            }
917
-
918
-            list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
919
-            if (null !== $findToken) {
920
-                $insertPos = $this->origTokens->findRight($pos, $findToken) + 1;
921
-                $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
922
-                $pos = $insertPos;
923
-            }
924
-
925
-            $first = true;
926
-            $result .= $extraLeft;
927
-            foreach ($delayedAdd as $delayedAddNode) {
928
-                if (!$first) {
929
-                    $result .= $insertStr;
930
-                    if ($insertNewline) {
931
-                        $result .= $this->nl;
932
-                    }
933
-                }
934
-                $result .= $this->p($delayedAddNode, true);
935
-                $first = false;
936
-            }
937
-            $result .= $extraRight === "\n" ? $this->nl : $extraRight;
938
-        }
939
-
940
-        return $result;
941
-    }
942
-
943
-    /**
944
-     * Print node with fixups.
945
-     *
946
-     * Fixups here refer to the addition of extra parentheses, braces or other characters, that
947
-     * are required to preserve program semantics in a certain context (e.g. to maintain precedence
948
-     * or because only certain expressions are allowed in certain places).
949
-     *
950
-     * @param int         $fixup       Fixup type
951
-     * @param Node        $subNode     Subnode to print
952
-     * @param string|null $parentClass Class of parent node
953
-     * @param int         $subStartPos Original start pos of subnode
954
-     * @param int         $subEndPos   Original end pos of subnode
955
-     *
956
-     * @return string Result of fixed-up print of subnode
957
-     */
958
-    protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string {
959
-        switch ($fixup) {
960
-            case self::FIXUP_PREC_LEFT:
961
-            case self::FIXUP_PREC_RIGHT:
962
-                if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
963
-                    list($precedence, $associativity) = $this->precedenceMap[$parentClass];
964
-                    return $this->pPrec($subNode, $precedence, $associativity,
965
-                        $fixup === self::FIXUP_PREC_LEFT ? -1 : 1);
966
-                }
967
-                break;
968
-            case self::FIXUP_CALL_LHS:
969
-                if ($this->callLhsRequiresParens($subNode)
970
-                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
971
-                ) {
972
-                    return '(' . $this->p($subNode) . ')';
973
-                }
974
-                break;
975
-            case self::FIXUP_DEREF_LHS:
976
-                if ($this->dereferenceLhsRequiresParens($subNode)
977
-                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
978
-                ) {
979
-                    return '(' . $this->p($subNode) . ')';
980
-                }
981
-                break;
982
-            case self::FIXUP_STATIC_DEREF_LHS:
983
-                if ($this->staticDereferenceLhsRequiresParens($subNode)
984
-                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
985
-                ) {
986
-                    return '(' . $this->p($subNode) . ')';
987
-                }
988
-                break;
989
-            case self::FIXUP_NEW:
990
-                if ($this->newOperandRequiresParens($subNode)
991
-                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
992
-                    return '(' . $this->p($subNode) . ')';
993
-                }
994
-                break;
995
-            case self::FIXUP_BRACED_NAME:
996
-            case self::FIXUP_VAR_BRACED_NAME:
997
-                if ($subNode instanceof Expr
998
-                    && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
999
-                ) {
1000
-                    return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
1001
-                        . '{' . $this->p($subNode) . '}';
1002
-                }
1003
-                break;
1004
-            case self::FIXUP_ENCAPSED:
1005
-                if (!$subNode instanceof Scalar\EncapsedStringPart
1006
-                    && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
1007
-                ) {
1008
-                    return '{' . $this->p($subNode) . '}';
1009
-                }
1010
-                break;
1011
-            default:
1012
-                throw new \Exception('Cannot happen');
1013
-        }
1014
-
1015
-        // Nothing special to do
1016
-        return $this->p($subNode);
1017
-    }
1018
-
1019
-    /**
1020
-     * Appends to a string, ensuring whitespace between label characters.
1021
-     *
1022
-     * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x".
1023
-     * Without safeAppend the result would be "echox", which does not preserve semantics.
1024
-     *
1025
-     * @param string $str
1026
-     * @param string $append
1027
-     */
1028
-    protected function safeAppend(string &$str, string $append) {
1029
-        if ($str === "") {
1030
-            $str = $append;
1031
-            return;
1032
-        }
1033
-
1034
-        if ($append === "") {
1035
-            return;
1036
-        }
1037
-
1038
-        if (!$this->labelCharMap[$append[0]]
1039
-                || !$this->labelCharMap[$str[\strlen($str) - 1]]) {
1040
-            $str .= $append;
1041
-        } else {
1042
-            $str .= " " . $append;
1043
-        }
1044
-    }
1045
-
1046
-    /**
1047
-     * Determines whether the LHS of a call must be wrapped in parenthesis.
1048
-     *
1049
-     * @param Node $node LHS of a call
1050
-     *
1051
-     * @return bool Whether parentheses are required
1052
-     */
1053
-    protected function callLhsRequiresParens(Node $node) : bool {
1054
-        return !($node instanceof Node\Name
1055
-            || $node instanceof Expr\Variable
1056
-            || $node instanceof Expr\ArrayDimFetch
1057
-            || $node instanceof Expr\FuncCall
1058
-            || $node instanceof Expr\MethodCall
1059
-            || $node instanceof Expr\NullsafeMethodCall
1060
-            || $node instanceof Expr\StaticCall
1061
-            || $node instanceof Expr\Array_);
1062
-    }
1063
-
1064
-    /**
1065
-     * Determines whether the LHS of an array/object operation must be wrapped in parentheses.
1066
-     *
1067
-     * @param Node $node LHS of dereferencing operation
1068
-     *
1069
-     * @return bool Whether parentheses are required
1070
-     */
1071
-    protected function dereferenceLhsRequiresParens(Node $node) : bool {
1072
-        // A constant can occur on the LHS of an array/object deref, but not a static deref.
1073
-        return $this->staticDereferenceLhsRequiresParens($node)
1074
-            && !$node instanceof Expr\ConstFetch;
1075
-    }
1076
-
1077
-    /**
1078
-     * Determines whether the LHS of a static operation must be wrapped in parentheses.
1079
-     *
1080
-     * @param Node $node LHS of dereferencing operation
1081
-     *
1082
-     * @return bool Whether parentheses are required
1083
-     */
1084
-    protected function staticDereferenceLhsRequiresParens(Node $node): bool {
1085
-        return !($node instanceof Expr\Variable
1086
-            || $node instanceof Node\Name
1087
-            || $node instanceof Expr\ArrayDimFetch
1088
-            || $node instanceof Expr\PropertyFetch
1089
-            || $node instanceof Expr\NullsafePropertyFetch
1090
-            || $node instanceof Expr\StaticPropertyFetch
1091
-            || $node instanceof Expr\FuncCall
1092
-            || $node instanceof Expr\MethodCall
1093
-            || $node instanceof Expr\NullsafeMethodCall
1094
-            || $node instanceof Expr\StaticCall
1095
-            || $node instanceof Expr\Array_
1096
-            || $node instanceof Scalar\String_
1097
-            || $node instanceof Expr\ClassConstFetch);
1098
-    }
1099
-
1100
-    /**
1101
-     * Determines whether an expression used in "new" or "instanceof" requires parentheses.
1102
-     *
1103
-     * @param Node $node New or instanceof operand
1104
-     *
1105
-     * @return bool Whether parentheses are required
1106
-     */
1107
-    protected function newOperandRequiresParens(Node $node): bool {
1108
-        if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
1109
-            return false;
1110
-        }
1111
-        if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
1112
-            $node instanceof Expr\NullsafePropertyFetch
1113
-        ) {
1114
-            return $this->newOperandRequiresParens($node->var);
1115
-        }
1116
-        if ($node instanceof Expr\StaticPropertyFetch) {
1117
-            return $this->newOperandRequiresParens($node->class);
1118
-        }
1119
-        return true;
1120
-    }
1121
-
1122
-    /**
1123
-     * Print modifiers, including trailing whitespace.
1124
-     *
1125
-     * @param int $modifiers Modifier mask to print
1126
-     *
1127
-     * @return string Printed modifiers
1128
-     */
1129
-    protected function pModifiers(int $modifiers) {
1130
-        return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC    ? 'public '    : '')
1131
-             . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
1132
-             . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE   ? 'private '   : '')
1133
-             . ($modifiers & Stmt\Class_::MODIFIER_STATIC    ? 'static '    : '')
1134
-             . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT  ? 'abstract '  : '')
1135
-             . ($modifiers & Stmt\Class_::MODIFIER_FINAL     ? 'final '     : '')
1136
-             . ($modifiers & Stmt\Class_::MODIFIER_READONLY  ? 'readonly '  : '');
1137
-    }
1138
-
1139
-    /**
1140
-     * Determine whether a list of nodes uses multiline formatting.
1141
-     *
1142
-     * @param (Node|null)[] $nodes Node list
1143
-     *
1144
-     * @return bool Whether multiline formatting is used
1145
-     */
1146
-    protected function isMultiline(array $nodes) : bool {
1147
-        if (\count($nodes) < 2) {
1148
-            return false;
1149
-        }
1150
-
1151
-        $pos = -1;
1152
-        foreach ($nodes as $node) {
1153
-            if (null === $node) {
1154
-                continue;
1155
-            }
1156
-
1157
-            $endPos = $node->getEndTokenPos() + 1;
1158
-            if ($pos >= 0) {
1159
-                $text = $this->origTokens->getTokenCode($pos, $endPos, 0);
1160
-                if (false === strpos($text, "\n")) {
1161
-                    // We require that a newline is present between *every* item. If the formatting
1162
-                    // is inconsistent, with only some items having newlines, we don't consider it
1163
-                    // as multiline
1164
-                    return false;
1165
-                }
1166
-            }
1167
-            $pos = $endPos;
1168
-        }
1169
-
1170
-        return true;
1171
-    }
1172
-
1173
-    /**
1174
-     * Lazily initializes label char map.
1175
-     *
1176
-     * The label char map determines whether a certain character may occur in a label.
1177
-     */
1178
-    protected function initializeLabelCharMap() {
1179
-        if ($this->labelCharMap) return;
1180
-
1181
-        $this->labelCharMap = [];
1182
-        for ($i = 0; $i < 256; $i++) {
1183
-            // Since PHP 7.1 The lower range is 0x80. However, we also want to support code for
1184
-            // older versions.
1185
-            $chr = chr($i);
1186
-            $this->labelCharMap[$chr] = $i >= 0x7f || ctype_alnum($chr);
1187
-        }
1188
-    }
1189
-
1190
-    /**
1191
-     * Lazily initializes node list differ.
1192
-     *
1193
-     * The node list differ is used to determine differences between two array subnodes.
1194
-     */
1195
-    protected function initializeNodeListDiffer() {
1196
-        if ($this->nodeListDiffer) return;
1197
-
1198
-        $this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
1199
-            if ($a instanceof Node && $b instanceof Node) {
1200
-                return $a === $b->getAttribute('origNode');
1201
-            }
1202
-            // Can happen for array destructuring
1203
-            return $a === null && $b === null;
1204
-        });
1205
-    }
1206
-
1207
-    /**
1208
-     * Lazily initializes fixup map.
1209
-     *
1210
-     * The fixup map is used to determine whether a certain subnode of a certain node may require
1211
-     * some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
1212
-     */
1213
-    protected function initializeFixupMap() {
1214
-        if ($this->fixupMap) return;
1215
-
1216
-        $this->fixupMap = [
1217
-            Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT],
1218
-            Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT],
1219
-            Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT],
1220
-            Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
1221
-            Expr\Instanceof_::class => [
1222
-                'expr' => self::FIXUP_PREC_LEFT,
1223
-                'class' => self::FIXUP_NEW,
1224
-            ],
1225
-            Expr\Ternary::class => [
1226
-                'cond' => self::FIXUP_PREC_LEFT,
1227
-                'else' => self::FIXUP_PREC_RIGHT,
1228
-            ],
1229
-
1230
-            Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
1231
-            Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
1232
-            Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
1233
-            Expr\ClassConstFetch::class => [
1234
-                'class' => self::FIXUP_STATIC_DEREF_LHS,
1235
-                'name' => self::FIXUP_BRACED_NAME,
1236
-            ],
1237
-            Expr\New_::class => ['class' => self::FIXUP_NEW],
1238
-            Expr\MethodCall::class => [
1239
-                'var' => self::FIXUP_DEREF_LHS,
1240
-                'name' => self::FIXUP_BRACED_NAME,
1241
-            ],
1242
-            Expr\NullsafeMethodCall::class => [
1243
-                'var' => self::FIXUP_DEREF_LHS,
1244
-                'name' => self::FIXUP_BRACED_NAME,
1245
-            ],
1246
-            Expr\StaticPropertyFetch::class => [
1247
-                'class' => self::FIXUP_STATIC_DEREF_LHS,
1248
-                'name' => self::FIXUP_VAR_BRACED_NAME,
1249
-            ],
1250
-            Expr\PropertyFetch::class => [
1251
-                'var' => self::FIXUP_DEREF_LHS,
1252
-                'name' => self::FIXUP_BRACED_NAME,
1253
-            ],
1254
-            Expr\NullsafePropertyFetch::class => [
1255
-                'var' => self::FIXUP_DEREF_LHS,
1256
-                'name' => self::FIXUP_BRACED_NAME,
1257
-            ],
1258
-            Scalar\Encapsed::class => [
1259
-                'parts' => self::FIXUP_ENCAPSED,
1260
-            ],
1261
-        ];
1262
-
1263
-        $binaryOps = [
1264
-            BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class,
1265
-            BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class,
1266
-            BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class,
1267
-            BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class,
1268
-            BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class,
1269
-            BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
1270
-            BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
1271
-            BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
1272
-            BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class,
1273
-        ];
1274
-        foreach ($binaryOps as $binaryOp) {
1275
-            $this->fixupMap[$binaryOp] = [
1276
-                'left' => self::FIXUP_PREC_LEFT,
1277
-                'right' => self::FIXUP_PREC_RIGHT
1278
-            ];
1279
-        }
1280
-
1281
-        $assignOps = [
1282
-            Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
1283
-            AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
1284
-            AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
1285
-            AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class
1286
-        ];
1287
-        foreach ($assignOps as $assignOp) {
1288
-            $this->fixupMap[$assignOp] = [
1289
-                'var' => self::FIXUP_PREC_LEFT,
1290
-                'expr' => self::FIXUP_PREC_RIGHT,
1291
-            ];
1292
-        }
1293
-
1294
-        $prefixOps = [
1295
-            Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class,
1296
-            Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class,
1297
-            Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class,
1298
-            Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class,
1299
-        ];
1300
-        foreach ($prefixOps as $prefixOp) {
1301
-            $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT];
1302
-        }
1303
-    }
1304
-
1305
-    /**
1306
-     * Lazily initializes the removal map.
1307
-     *
1308
-     * The removal map is used to determine which additional tokens should be removed when a
1309
-     * certain node is replaced by null.
1310
-     */
1311
-    protected function initializeRemovalMap() {
1312
-        if ($this->removalMap) return;
1313
-
1314
-        $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
1315
-        $stripLeft = ['left' => \T_WHITESPACE];
1316
-        $stripRight = ['right' => \T_WHITESPACE];
1317
-        $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW];
1318
-        $stripColon = ['left' => ':'];
1319
-        $stripEquals = ['left' => '='];
1320
-        $this->removalMap = [
1321
-            'Expr_ArrayDimFetch->dim' => $stripBoth,
1322
-            'Expr_ArrayItem->key' => $stripDoubleArrow,
1323
-            'Expr_ArrowFunction->returnType' => $stripColon,
1324
-            'Expr_Closure->returnType' => $stripColon,
1325
-            'Expr_Exit->expr' => $stripBoth,
1326
-            'Expr_Ternary->if' => $stripBoth,
1327
-            'Expr_Yield->key' => $stripDoubleArrow,
1328
-            'Expr_Yield->value' => $stripBoth,
1329
-            'Param->type' => $stripRight,
1330
-            'Param->default' => $stripEquals,
1331
-            'Stmt_Break->num' => $stripBoth,
1332
-            'Stmt_Catch->var' => $stripLeft,
1333
-            'Stmt_ClassConst->type' => $stripRight,
1334
-            'Stmt_ClassMethod->returnType' => $stripColon,
1335
-            'Stmt_Class->extends' => ['left' => \T_EXTENDS],
1336
-            'Stmt_Enum->scalarType' => $stripColon,
1337
-            'Stmt_EnumCase->expr' => $stripEquals,
1338
-            'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
1339
-            'Stmt_Continue->num' => $stripBoth,
1340
-            'Stmt_Foreach->keyVar' => $stripDoubleArrow,
1341
-            'Stmt_Function->returnType' => $stripColon,
1342
-            'Stmt_If->else' => $stripLeft,
1343
-            'Stmt_Namespace->name' => $stripLeft,
1344
-            'Stmt_Property->type' => $stripRight,
1345
-            'Stmt_PropertyProperty->default' => $stripEquals,
1346
-            'Stmt_Return->expr' => $stripBoth,
1347
-            'Stmt_StaticVar->default' => $stripEquals,
1348
-            'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft,
1349
-            'Stmt_TryCatch->finally' => $stripLeft,
1350
-            // 'Stmt_Case->cond': Replace with "default"
1351
-            // 'Stmt_Class->name': Unclear what to do
1352
-            // 'Stmt_Declare->stmts': Not a plain node
1353
-            // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
1354
-        ];
1355
-    }
1356
-
1357
-    protected function initializeInsertionMap() {
1358
-        if ($this->insertionMap) return;
1359
-
1360
-        // TODO: "yield" where both key and value are inserted doesn't work
1361
-        // [$find, $beforeToken, $extraLeft, $extraRight]
1362
-        $this->insertionMap = [
1363
-            'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
1364
-            'Expr_ArrayItem->key' => [null, false, null, ' => '],
1365
-            'Expr_ArrowFunction->returnType' => [')', false, ' : ', null],
1366
-            'Expr_Closure->returnType' => [')', false, ' : ', null],
1367
-            'Expr_Ternary->if' => ['?', false, ' ', ' '],
1368
-            'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
1369
-            'Expr_Yield->value' => [\T_YIELD, false, ' ', null],
1370
-            'Param->type' => [null, false, null, ' '],
1371
-            'Param->default' => [null, false, ' = ', null],
1372
-            'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
1373
-            'Stmt_Catch->var' => [null, false, ' ', null],
1374
-            'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
1375
-            'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
1376
-            'Stmt_Class->extends' => [null, false, ' extends ', null],
1377
-            'Stmt_Enum->scalarType' => [null, false, ' : ', null],
1378
-            'Stmt_EnumCase->expr' => [null, false, ' = ', null],
1379
-            'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null],
1380
-            'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
1381
-            'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
1382
-            'Stmt_Function->returnType' => [')', false, ' : ', null],
1383
-            'Stmt_If->else' => [null, false, ' ', null],
1384
-            'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null],
1385
-            'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '],
1386
-            'Stmt_PropertyProperty->default' => [null, false, ' = ', null],
1387
-            'Stmt_Return->expr' => [\T_RETURN, false, ' ', null],
1388
-            'Stmt_StaticVar->default' => [null, false, ' = ', null],
1389
-            //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO
1390
-            'Stmt_TryCatch->finally' => [null, false, ' ', null],
1391
-
1392
-            // 'Expr_Exit->expr': Complicated due to optional ()
1393
-            // 'Stmt_Case->cond': Conversion from default to case
1394
-            // 'Stmt_Class->name': Unclear
1395
-            // 'Stmt_Declare->stmts': Not a proper node
1396
-            // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
1397
-        ];
1398
-    }
1399
-
1400
-    protected function initializeListInsertionMap() {
1401
-        if ($this->listInsertionMap) return;
1402
-
1403
-        $this->listInsertionMap = [
1404
-            // special
1405
-            //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
1406
-            //'Scalar_Encapsed->parts' => '',
1407
-            'Stmt_Catch->types' => '|',
1408
-            'UnionType->types' => '|',
1409
-            'IntersectionType->types' => '&',
1410
-            'Stmt_If->elseifs' => ' ',
1411
-            'Stmt_TryCatch->catches' => ' ',
1412
-
1413
-            // comma-separated lists
1414
-            'Expr_Array->items' => ', ',
1415
-            'Expr_ArrowFunction->params' => ', ',
1416
-            'Expr_Closure->params' => ', ',
1417
-            'Expr_Closure->uses' => ', ',
1418
-            'Expr_FuncCall->args' => ', ',
1419
-            'Expr_Isset->vars' => ', ',
1420
-            'Expr_List->items' => ', ',
1421
-            'Expr_MethodCall->args' => ', ',
1422
-            'Expr_NullsafeMethodCall->args' => ', ',
1423
-            'Expr_New->args' => ', ',
1424
-            'Expr_PrintableNewAnonClass->args' => ', ',
1425
-            'Expr_StaticCall->args' => ', ',
1426
-            'Stmt_ClassConst->consts' => ', ',
1427
-            'Stmt_ClassMethod->params' => ', ',
1428
-            'Stmt_Class->implements' => ', ',
1429
-            'Stmt_Enum->implements' => ', ',
1430
-            'Expr_PrintableNewAnonClass->implements' => ', ',
1431
-            'Stmt_Const->consts' => ', ',
1432
-            'Stmt_Declare->declares' => ', ',
1433
-            'Stmt_Echo->exprs' => ', ',
1434
-            'Stmt_For->init' => ', ',
1435
-            'Stmt_For->cond' => ', ',
1436
-            'Stmt_For->loop' => ', ',
1437
-            'Stmt_Function->params' => ', ',
1438
-            'Stmt_Global->vars' => ', ',
1439
-            'Stmt_GroupUse->uses' => ', ',
1440
-            'Stmt_Interface->extends' => ', ',
1441
-            'Stmt_Match->arms' => ', ',
1442
-            'Stmt_Property->props' => ', ',
1443
-            'Stmt_StaticVar->vars' => ', ',
1444
-            'Stmt_TraitUse->traits' => ', ',
1445
-            'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ',
1446
-            'Stmt_Unset->vars' => ', ',
1447
-            'Stmt_Use->uses' => ', ',
1448
-            'MatchArm->conds' => ', ',
1449
-            'AttributeGroup->attrs' => ', ',
1450
-
1451
-            // statement lists
1452
-            'Expr_Closure->stmts' => "\n",
1453
-            'Stmt_Case->stmts' => "\n",
1454
-            'Stmt_Catch->stmts' => "\n",
1455
-            'Stmt_Class->stmts' => "\n",
1456
-            'Stmt_Enum->stmts' => "\n",
1457
-            'Expr_PrintableNewAnonClass->stmts' => "\n",
1458
-            'Stmt_Interface->stmts' => "\n",
1459
-            'Stmt_Trait->stmts' => "\n",
1460
-            'Stmt_ClassMethod->stmts' => "\n",
1461
-            'Stmt_Declare->stmts' => "\n",
1462
-            'Stmt_Do->stmts' => "\n",
1463
-            'Stmt_ElseIf->stmts' => "\n",
1464
-            'Stmt_Else->stmts' => "\n",
1465
-            'Stmt_Finally->stmts' => "\n",
1466
-            'Stmt_Foreach->stmts' => "\n",
1467
-            'Stmt_For->stmts' => "\n",
1468
-            'Stmt_Function->stmts' => "\n",
1469
-            'Stmt_If->stmts' => "\n",
1470
-            'Stmt_Namespace->stmts' => "\n",
1471
-            'Stmt_Class->attrGroups' => "\n",
1472
-            'Stmt_Enum->attrGroups' => "\n",
1473
-            'Stmt_EnumCase->attrGroups' => "\n",
1474
-            'Stmt_Interface->attrGroups' => "\n",
1475
-            'Stmt_Trait->attrGroups' => "\n",
1476
-            'Stmt_Function->attrGroups' => "\n",
1477
-            'Stmt_ClassMethod->attrGroups' => "\n",
1478
-            'Stmt_ClassConst->attrGroups' => "\n",
1479
-            'Stmt_Property->attrGroups' => "\n",
1480
-            'Expr_PrintableNewAnonClass->attrGroups' => ' ',
1481
-            'Expr_Closure->attrGroups' => ' ',
1482
-            'Expr_ArrowFunction->attrGroups' => ' ',
1483
-            'Param->attrGroups' => ' ',
1484
-            'Stmt_Switch->cases' => "\n",
1485
-            'Stmt_TraitUse->adaptations' => "\n",
1486
-            'Stmt_TryCatch->stmts' => "\n",
1487
-            'Stmt_While->stmts' => "\n",
1488
-
1489
-            // dummy for top-level context
1490
-            'File->stmts' => "\n",
1491
-        ];
1492
-    }
1493
-
1494
-    protected function initializeEmptyListInsertionMap() {
1495
-        if ($this->emptyListInsertionMap) return;
1496
-
1497
-        // TODO Insertion into empty statement lists.
1498
-
1499
-        // [$find, $extraLeft, $extraRight]
1500
-        $this->emptyListInsertionMap = [
1501
-            'Expr_ArrowFunction->params' => ['(', '', ''],
1502
-            'Expr_Closure->uses' => [')', ' use(', ')'],
1503
-            'Expr_Closure->params' => ['(', '', ''],
1504
-            'Expr_FuncCall->args' => ['(', '', ''],
1505
-            'Expr_MethodCall->args' => ['(', '', ''],
1506
-            'Expr_NullsafeMethodCall->args' => ['(', '', ''],
1507
-            'Expr_New->args' => ['(', '', ''],
1508
-            'Expr_PrintableNewAnonClass->args' => ['(', '', ''],
1509
-            'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''],
1510
-            'Expr_StaticCall->args' => ['(', '', ''],
1511
-            'Stmt_Class->implements' => [null, ' implements ', ''],
1512
-            'Stmt_Enum->implements' => [null, ' implements ', ''],
1513
-            'Stmt_ClassMethod->params' => ['(', '', ''],
1514
-            'Stmt_Interface->extends' => [null, ' extends ', ''],
1515
-            'Stmt_Function->params' => ['(', '', ''],
1516
-            'Stmt_Interface->attrGroups' => [null, '', "\n"],
1517
-            'Stmt_Class->attrGroups' => [null, '', "\n"],
1518
-            'Stmt_ClassConst->attrGroups' => [null, '', "\n"],
1519
-            'Stmt_ClassMethod->attrGroups' => [null, '', "\n"],
1520
-            'Stmt_Function->attrGroups' => [null, '', "\n"],
1521
-            'Stmt_Property->attrGroups' => [null, '', "\n"],
1522
-            'Stmt_Trait->attrGroups' => [null, '', "\n"],
1523
-            'Expr_ArrowFunction->attrGroups' => [null, '', ' '],
1524
-            'Expr_Closure->attrGroups' => [null, '', ' '],
1525
-            'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', ''],
1526
-
1527
-            /* These cannot be empty to start with:
17
+	const FIXUP_PREC_LEFT       = 0; // LHS operand affected by precedence
18
+	const FIXUP_PREC_RIGHT      = 1; // RHS operand affected by precedence
19
+	const FIXUP_CALL_LHS        = 2; // LHS of call
20
+	const FIXUP_DEREF_LHS       = 3; // LHS of dereferencing operation
21
+	const FIXUP_BRACED_NAME     = 4; // Name operand that may require bracing
22
+	const FIXUP_VAR_BRACED_NAME = 5; // Name operand that may require ${} bracing
23
+	const FIXUP_ENCAPSED        = 6; // Encapsed string part
24
+	const FIXUP_NEW             = 7; // New/instanceof operand
25
+	const FIXUP_STATIC_DEREF_LHS = 8; // LHS of static dereferencing operation
26
+
27
+	protected $precedenceMap = [
28
+		// [precedence, associativity]
29
+		// where for precedence -1 is %left, 0 is %nonassoc and 1 is %right
30
+		BinaryOp\Pow::class            => [  0,  1],
31
+		Expr\BitwiseNot::class         => [ 10,  1],
32
+		Expr\PreInc::class             => [ 10,  1],
33
+		Expr\PreDec::class             => [ 10,  1],
34
+		Expr\PostInc::class            => [ 10, -1],
35
+		Expr\PostDec::class            => [ 10, -1],
36
+		Expr\UnaryPlus::class          => [ 10,  1],
37
+		Expr\UnaryMinus::class         => [ 10,  1],
38
+		Cast\Int_::class               => [ 10,  1],
39
+		Cast\Double::class             => [ 10,  1],
40
+		Cast\String_::class            => [ 10,  1],
41
+		Cast\Array_::class             => [ 10,  1],
42
+		Cast\Object_::class            => [ 10,  1],
43
+		Cast\Bool_::class              => [ 10,  1],
44
+		Cast\Unset_::class             => [ 10,  1],
45
+		Expr\ErrorSuppress::class      => [ 10,  1],
46
+		Expr\Instanceof_::class        => [ 20,  0],
47
+		Expr\BooleanNot::class         => [ 30,  1],
48
+		BinaryOp\Mul::class            => [ 40, -1],
49
+		BinaryOp\Div::class            => [ 40, -1],
50
+		BinaryOp\Mod::class            => [ 40, -1],
51
+		BinaryOp\Plus::class           => [ 50, -1],
52
+		BinaryOp\Minus::class          => [ 50, -1],
53
+		BinaryOp\Concat::class         => [ 50, -1],
54
+		BinaryOp\ShiftLeft::class      => [ 60, -1],
55
+		BinaryOp\ShiftRight::class     => [ 60, -1],
56
+		BinaryOp\Smaller::class        => [ 70,  0],
57
+		BinaryOp\SmallerOrEqual::class => [ 70,  0],
58
+		BinaryOp\Greater::class        => [ 70,  0],
59
+		BinaryOp\GreaterOrEqual::class => [ 70,  0],
60
+		BinaryOp\Equal::class          => [ 80,  0],
61
+		BinaryOp\NotEqual::class       => [ 80,  0],
62
+		BinaryOp\Identical::class      => [ 80,  0],
63
+		BinaryOp\NotIdentical::class   => [ 80,  0],
64
+		BinaryOp\Spaceship::class      => [ 80,  0],
65
+		BinaryOp\BitwiseAnd::class     => [ 90, -1],
66
+		BinaryOp\BitwiseXor::class     => [100, -1],
67
+		BinaryOp\BitwiseOr::class      => [110, -1],
68
+		BinaryOp\BooleanAnd::class     => [120, -1],
69
+		BinaryOp\BooleanOr::class      => [130, -1],
70
+		BinaryOp\Coalesce::class       => [140,  1],
71
+		Expr\Ternary::class            => [150,  0],
72
+		// parser uses %left for assignments, but they really behave as %right
73
+		Expr\Assign::class             => [160,  1],
74
+		Expr\AssignRef::class          => [160,  1],
75
+		AssignOp\Plus::class           => [160,  1],
76
+		AssignOp\Minus::class          => [160,  1],
77
+		AssignOp\Mul::class            => [160,  1],
78
+		AssignOp\Div::class            => [160,  1],
79
+		AssignOp\Concat::class         => [160,  1],
80
+		AssignOp\Mod::class            => [160,  1],
81
+		AssignOp\BitwiseAnd::class     => [160,  1],
82
+		AssignOp\BitwiseOr::class      => [160,  1],
83
+		AssignOp\BitwiseXor::class     => [160,  1],
84
+		AssignOp\ShiftLeft::class      => [160,  1],
85
+		AssignOp\ShiftRight::class     => [160,  1],
86
+		AssignOp\Pow::class            => [160,  1],
87
+		AssignOp\Coalesce::class       => [160,  1],
88
+		Expr\YieldFrom::class          => [165,  1],
89
+		Expr\Print_::class             => [168,  1],
90
+		BinaryOp\LogicalAnd::class     => [170, -1],
91
+		BinaryOp\LogicalXor::class     => [180, -1],
92
+		BinaryOp\LogicalOr::class      => [190, -1],
93
+		Expr\Include_::class           => [200, -1],
94
+	];
95
+
96
+	/** @var int Current indentation level. */
97
+	protected $indentLevel;
98
+	/** @var string Newline including current indentation. */
99
+	protected $nl;
100
+	/** @var string Token placed at end of doc string to ensure it is followed by a newline. */
101
+	protected $docStringEndToken;
102
+	/** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
103
+	protected $canUseSemicolonNamespaces;
104
+	/** @var array Pretty printer options */
105
+	protected $options;
106
+
107
+	/** @var TokenStream Original tokens for use in format-preserving pretty print */
108
+	protected $origTokens;
109
+	/** @var Internal\Differ Differ for node lists */
110
+	protected $nodeListDiffer;
111
+	/** @var bool[] Map determining whether a certain character is a label character */
112
+	protected $labelCharMap;
113
+	/**
114
+	 * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used
115
+	 *              during format-preserving prints to place additional parens/braces if necessary.
116
+	 */
117
+	protected $fixupMap;
118
+	/**
119
+	 * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r],
120
+	 *              where $l and $r specify the token type that needs to be stripped when removing
121
+	 *              this node.
122
+	 */
123
+	protected $removalMap;
124
+	/**
125
+	 * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight].
126
+	 *              $find is an optional token after which the insertion occurs. $extraLeft/Right
127
+	 *              are optionally added before/after the main insertions.
128
+	 */
129
+	protected $insertionMap;
130
+	/**
131
+	 * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted
132
+	 *               between elements of this list subnode.
133
+	 */
134
+	protected $listInsertionMap;
135
+	protected $emptyListInsertionMap;
136
+	/** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers
137
+	 *             should be reprinted. */
138
+	protected $modifierChangeMap;
139
+
140
+	/**
141
+	 * Creates a pretty printer instance using the given options.
142
+	 *
143
+	 * Supported options:
144
+	 *  * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array
145
+	 *                                    syntax, if the node does not specify a format.
146
+	 *
147
+	 * @param array $options Dictionary of formatting options
148
+	 */
149
+	public function __construct(array $options = []) {
150
+		$this->docStringEndToken = '_DOC_STRING_END_' . mt_rand();
151
+
152
+		$defaultOptions = ['shortArraySyntax' => false];
153
+		$this->options = $options + $defaultOptions;
154
+	}
155
+
156
+	/**
157
+	 * Reset pretty printing state.
158
+	 */
159
+	protected function resetState() {
160
+		$this->indentLevel = 0;
161
+		$this->nl = "\n";
162
+		$this->origTokens = null;
163
+	}
164
+
165
+	/**
166
+	 * Set indentation level
167
+	 *
168
+	 * @param int $level Level in number of spaces
169
+	 */
170
+	protected function setIndentLevel(int $level) {
171
+		$this->indentLevel = $level;
172
+		$this->nl = "\n" . \str_repeat(' ', $level);
173
+	}
174
+
175
+	/**
176
+	 * Increase indentation level.
177
+	 */
178
+	protected function indent() {
179
+		$this->indentLevel += 4;
180
+		$this->nl .= '    ';
181
+	}
182
+
183
+	/**
184
+	 * Decrease indentation level.
185
+	 */
186
+	protected function outdent() {
187
+		assert($this->indentLevel >= 4);
188
+		$this->indentLevel -= 4;
189
+		$this->nl = "\n" . str_repeat(' ', $this->indentLevel);
190
+	}
191
+
192
+	/**
193
+	 * Pretty prints an array of statements.
194
+	 *
195
+	 * @param Node[] $stmts Array of statements
196
+	 *
197
+	 * @return string Pretty printed statements
198
+	 */
199
+	public function prettyPrint(array $stmts) : string {
200
+		$this->resetState();
201
+		$this->preprocessNodes($stmts);
202
+
203
+		return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
204
+	}
205
+
206
+	/**
207
+	 * Pretty prints an expression.
208
+	 *
209
+	 * @param Expr $node Expression node
210
+	 *
211
+	 * @return string Pretty printed node
212
+	 */
213
+	public function prettyPrintExpr(Expr $node) : string {
214
+		$this->resetState();
215
+		return $this->handleMagicTokens($this->p($node));
216
+	}
217
+
218
+	/**
219
+	 * Pretty prints a file of statements (includes the opening <?php tag if it is required).
220
+	 *
221
+	 * @param Node[] $stmts Array of statements
222
+	 *
223
+	 * @return string Pretty printed statements
224
+	 */
225
+	public function prettyPrintFile(array $stmts) : string {
226
+		if (!$stmts) {
227
+			return "<?php\n\n";
228
+		}
229
+
230
+		$p = "<?php\n\n" . $this->prettyPrint($stmts);
231
+
232
+		if ($stmts[0] instanceof Stmt\InlineHTML) {
233
+			$p = preg_replace('/^<\?php\s+\?>\n?/', '', $p);
234
+		}
235
+		if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
236
+			$p = preg_replace('/<\?php$/', '', rtrim($p));
237
+		}
238
+
239
+		return $p;
240
+	}
241
+
242
+	/**
243
+	 * Preprocesses the top-level nodes to initialize pretty printer state.
244
+	 *
245
+	 * @param Node[] $nodes Array of nodes
246
+	 */
247
+	protected function preprocessNodes(array $nodes) {
248
+		/* We can use semicolon-namespaces unless there is a global namespace declaration */
249
+		$this->canUseSemicolonNamespaces = true;
250
+		foreach ($nodes as $node) {
251
+			if ($node instanceof Stmt\Namespace_ && null === $node->name) {
252
+				$this->canUseSemicolonNamespaces = false;
253
+				break;
254
+			}
255
+		}
256
+	}
257
+
258
+	/**
259
+	 * Handles (and removes) no-indent and doc-string-end tokens.
260
+	 *
261
+	 * @param string $str
262
+	 * @return string
263
+	 */
264
+	protected function handleMagicTokens(string $str) : string {
265
+		// Replace doc-string-end tokens with nothing or a newline
266
+		$str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
267
+		$str = str_replace($this->docStringEndToken, "\n", $str);
268
+
269
+		return $str;
270
+	}
271
+
272
+	/**
273
+	 * Pretty prints an array of nodes (statements) and indents them optionally.
274
+	 *
275
+	 * @param Node[] $nodes  Array of nodes
276
+	 * @param bool   $indent Whether to indent the printed nodes
277
+	 *
278
+	 * @return string Pretty printed statements
279
+	 */
280
+	protected function pStmts(array $nodes, bool $indent = true) : string {
281
+		if ($indent) {
282
+			$this->indent();
283
+		}
284
+
285
+		$result = '';
286
+		foreach ($nodes as $node) {
287
+			$comments = $node->getComments();
288
+			if ($comments) {
289
+				$result .= $this->nl . $this->pComments($comments);
290
+				if ($node instanceof Stmt\Nop) {
291
+					continue;
292
+				}
293
+			}
294
+
295
+			$result .= $this->nl . $this->p($node);
296
+		}
297
+
298
+		if ($indent) {
299
+			$this->outdent();
300
+		}
301
+
302
+		return $result;
303
+	}
304
+
305
+	/**
306
+	 * Pretty-print an infix operation while taking precedence into account.
307
+	 *
308
+	 * @param string $class          Node class of operator
309
+	 * @param Node   $leftNode       Left-hand side node
310
+	 * @param string $operatorString String representation of the operator
311
+	 * @param Node   $rightNode      Right-hand side node
312
+	 *
313
+	 * @return string Pretty printed infix operation
314
+	 */
315
+	protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string {
316
+		list($precedence, $associativity) = $this->precedenceMap[$class];
317
+
318
+		return $this->pPrec($leftNode, $precedence, $associativity, -1)
319
+			 . $operatorString
320
+			 . $this->pPrec($rightNode, $precedence, $associativity, 1);
321
+	}
322
+
323
+	/**
324
+	 * Pretty-print a prefix operation while taking precedence into account.
325
+	 *
326
+	 * @param string $class          Node class of operator
327
+	 * @param string $operatorString String representation of the operator
328
+	 * @param Node   $node           Node
329
+	 *
330
+	 * @return string Pretty printed prefix operation
331
+	 */
332
+	protected function pPrefixOp(string $class, string $operatorString, Node $node) : string {
333
+		list($precedence, $associativity) = $this->precedenceMap[$class];
334
+		return $operatorString . $this->pPrec($node, $precedence, $associativity, 1);
335
+	}
336
+
337
+	/**
338
+	 * Pretty-print a postfix operation while taking precedence into account.
339
+	 *
340
+	 * @param string $class          Node class of operator
341
+	 * @param string $operatorString String representation of the operator
342
+	 * @param Node   $node           Node
343
+	 *
344
+	 * @return string Pretty printed postfix operation
345
+	 */
346
+	protected function pPostfixOp(string $class, Node $node, string $operatorString) : string {
347
+		list($precedence, $associativity) = $this->precedenceMap[$class];
348
+		return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString;
349
+	}
350
+
351
+	/**
352
+	 * Prints an expression node with the least amount of parentheses necessary to preserve the meaning.
353
+	 *
354
+	 * @param Node $node                Node to pretty print
355
+	 * @param int  $parentPrecedence    Precedence of the parent operator
356
+	 * @param int  $parentAssociativity Associativity of parent operator
357
+	 *                                  (-1 is left, 0 is nonassoc, 1 is right)
358
+	 * @param int  $childPosition       Position of the node relative to the operator
359
+	 *                                  (-1 is left, 1 is right)
360
+	 *
361
+	 * @return string The pretty printed node
362
+	 */
363
+	protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string {
364
+		$class = \get_class($node);
365
+		if (isset($this->precedenceMap[$class])) {
366
+			$childPrecedence = $this->precedenceMap[$class][0];
367
+			if ($childPrecedence > $parentPrecedence
368
+				|| ($parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition)
369
+			) {
370
+				return '(' . $this->p($node) . ')';
371
+			}
372
+		}
373
+
374
+		return $this->p($node);
375
+	}
376
+
377
+	/**
378
+	 * Pretty prints an array of nodes and implodes the printed values.
379
+	 *
380
+	 * @param Node[] $nodes Array of Nodes to be printed
381
+	 * @param string $glue  Character to implode with
382
+	 *
383
+	 * @return string Imploded pretty printed nodes
384
+	 */
385
+	protected function pImplode(array $nodes, string $glue = '') : string {
386
+		$pNodes = [];
387
+		foreach ($nodes as $node) {
388
+			if (null === $node) {
389
+				$pNodes[] = '';
390
+			} else {
391
+				$pNodes[] = $this->p($node);
392
+			}
393
+		}
394
+
395
+		return implode($glue, $pNodes);
396
+	}
397
+
398
+	/**
399
+	 * Pretty prints an array of nodes and implodes the printed values with commas.
400
+	 *
401
+	 * @param Node[] $nodes Array of Nodes to be printed
402
+	 *
403
+	 * @return string Comma separated pretty printed nodes
404
+	 */
405
+	protected function pCommaSeparated(array $nodes) : string {
406
+		return $this->pImplode($nodes, ', ');
407
+	}
408
+
409
+	/**
410
+	 * Pretty prints a comma-separated list of nodes in multiline style, including comments.
411
+	 *
412
+	 * The result includes a leading newline and one level of indentation (same as pStmts).
413
+	 *
414
+	 * @param Node[] $nodes         Array of Nodes to be printed
415
+	 * @param bool   $trailingComma Whether to use a trailing comma
416
+	 *
417
+	 * @return string Comma separated pretty printed nodes in multiline style
418
+	 */
419
+	protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string {
420
+		$this->indent();
421
+
422
+		$result = '';
423
+		$lastIdx = count($nodes) - 1;
424
+		foreach ($nodes as $idx => $node) {
425
+			if ($node !== null) {
426
+				$comments = $node->getComments();
427
+				if ($comments) {
428
+					$result .= $this->nl . $this->pComments($comments);
429
+				}
430
+
431
+				$result .= $this->nl . $this->p($node);
432
+			} else {
433
+				$result .= $this->nl;
434
+			}
435
+			if ($trailingComma || $idx !== $lastIdx) {
436
+				$result .= ',';
437
+			}
438
+		}
439
+
440
+		$this->outdent();
441
+		return $result;
442
+	}
443
+
444
+	/**
445
+	 * Prints reformatted text of the passed comments.
446
+	 *
447
+	 * @param Comment[] $comments List of comments
448
+	 *
449
+	 * @return string Reformatted text of comments
450
+	 */
451
+	protected function pComments(array $comments) : string {
452
+		$formattedComments = [];
453
+
454
+		foreach ($comments as $comment) {
455
+			$formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
456
+		}
457
+
458
+		return implode($this->nl, $formattedComments);
459
+	}
460
+
461
+	/**
462
+	 * Perform a format-preserving pretty print of an AST.
463
+	 *
464
+	 * The format preservation is best effort. For some changes to the AST the formatting will not
465
+	 * be preserved (at least not locally).
466
+	 *
467
+	 * In order to use this method a number of prerequisites must be satisfied:
468
+	 *  * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
469
+	 *  * The CloningVisitor must be run on the AST prior to modification.
470
+	 *  * The original tokens must be provided, using the getTokens() method on the lexer.
471
+	 *
472
+	 * @param Node[] $stmts      Modified AST with links to original AST
473
+	 * @param Node[] $origStmts  Original AST with token offset information
474
+	 * @param array  $origTokens Tokens of the original code
475
+	 *
476
+	 * @return string
477
+	 */
478
+	public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string {
479
+		$this->initializeNodeListDiffer();
480
+		$this->initializeLabelCharMap();
481
+		$this->initializeFixupMap();
482
+		$this->initializeRemovalMap();
483
+		$this->initializeInsertionMap();
484
+		$this->initializeListInsertionMap();
485
+		$this->initializeEmptyListInsertionMap();
486
+		$this->initializeModifierChangeMap();
487
+
488
+		$this->resetState();
489
+		$this->origTokens = new TokenStream($origTokens);
490
+
491
+		$this->preprocessNodes($stmts);
492
+
493
+		$pos = 0;
494
+		$result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null);
495
+		if (null !== $result) {
496
+			$result .= $this->origTokens->getTokenCode($pos, count($origTokens), 0);
497
+		} else {
498
+			// Fallback
499
+			// TODO Add <?php properly
500
+			$result = "<?php\n" . $this->pStmts($stmts, false);
501
+		}
502
+
503
+		return ltrim($this->handleMagicTokens($result));
504
+	}
505
+
506
+	protected function pFallback(Node $node) {
507
+		return $this->{'p' . $node->getType()}($node);
508
+	}
509
+
510
+	/**
511
+	 * Pretty prints a node.
512
+	 *
513
+	 * This method also handles formatting preservation for nodes.
514
+	 *
515
+	 * @param Node $node Node to be pretty printed
516
+	 * @param bool $parentFormatPreserved Whether parent node has preserved formatting
517
+	 *
518
+	 * @return string Pretty printed node
519
+	 */
520
+	protected function p(Node $node, $parentFormatPreserved = false) : string {
521
+		// No orig tokens means this is a normal pretty print without preservation of formatting
522
+		if (!$this->origTokens) {
523
+			return $this->{'p' . $node->getType()}($node);
524
+		}
525
+
526
+		/** @var Node $origNode */
527
+		$origNode = $node->getAttribute('origNode');
528
+		if (null === $origNode) {
529
+			return $this->pFallback($node);
530
+		}
531
+
532
+		$class = \get_class($node);
533
+		\assert($class === \get_class($origNode));
534
+
535
+		$startPos = $origNode->getStartTokenPos();
536
+		$endPos = $origNode->getEndTokenPos();
537
+		\assert($startPos >= 0 && $endPos >= 0);
538
+
539
+		$fallbackNode = $node;
540
+		if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) {
541
+			// Normalize node structure of anonymous classes
542
+			$node = PrintableNewAnonClassNode::fromNewNode($node);
543
+			$origNode = PrintableNewAnonClassNode::fromNewNode($origNode);
544
+		}
545
+
546
+		// InlineHTML node does not contain closing and opening PHP tags. If the parent formatting
547
+		// is not preserved, then we need to use the fallback code to make sure the tags are
548
+		// printed.
549
+		if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) {
550
+			return $this->pFallback($fallbackNode);
551
+		}
552
+
553
+		$indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos);
554
+
555
+		$type = $node->getType();
556
+		$fixupInfo = $this->fixupMap[$class] ?? null;
557
+
558
+		$result = '';
559
+		$pos = $startPos;
560
+		foreach ($node->getSubNodeNames() as $subNodeName) {
561
+			$subNode = $node->$subNodeName;
562
+			$origSubNode = $origNode->$subNodeName;
563
+
564
+			if ((!$subNode instanceof Node && $subNode !== null)
565
+				|| (!$origSubNode instanceof Node && $origSubNode !== null)
566
+			) {
567
+				if ($subNode === $origSubNode) {
568
+					// Unchanged, can reuse old code
569
+					continue;
570
+				}
571
+
572
+				if (is_array($subNode) && is_array($origSubNode)) {
573
+					// Array subnode changed, we might be able to reconstruct it
574
+					$listResult = $this->pArray(
575
+						$subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName,
576
+						$fixupInfo[$subNodeName] ?? null
577
+					);
578
+					if (null === $listResult) {
579
+						return $this->pFallback($fallbackNode);
580
+					}
581
+
582
+					$result .= $listResult;
583
+					continue;
584
+				}
585
+
586
+				if (is_int($subNode) && is_int($origSubNode)) {
587
+					// Check if this is a modifier change
588
+					$key = $type . '->' . $subNodeName;
589
+					if (!isset($this->modifierChangeMap[$key])) {
590
+						return $this->pFallback($fallbackNode);
591
+					}
592
+
593
+					$findToken = $this->modifierChangeMap[$key];
594
+					$result .= $this->pModifiers($subNode);
595
+					$pos = $this->origTokens->findRight($pos, $findToken);
596
+					continue;
597
+				}
598
+
599
+				// If a non-node, non-array subnode changed, we don't be able to do a partial
600
+				// reconstructions, as we don't have enough offset information. Pretty print the
601
+				// whole node instead.
602
+				return $this->pFallback($fallbackNode);
603
+			}
604
+
605
+			$extraLeft = '';
606
+			$extraRight = '';
607
+			if ($origSubNode !== null) {
608
+				$subStartPos = $origSubNode->getStartTokenPos();
609
+				$subEndPos = $origSubNode->getEndTokenPos();
610
+				\assert($subStartPos >= 0 && $subEndPos >= 0);
611
+			} else {
612
+				if ($subNode === null) {
613
+					// Both null, nothing to do
614
+					continue;
615
+				}
616
+
617
+				// A node has been inserted, check if we have insertion information for it
618
+				$key = $type . '->' . $subNodeName;
619
+				if (!isset($this->insertionMap[$key])) {
620
+					return $this->pFallback($fallbackNode);
621
+				}
622
+
623
+				list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
624
+				if (null !== $findToken) {
625
+					$subStartPos = $this->origTokens->findRight($pos, $findToken)
626
+						+ (int) !$beforeToken;
627
+				} else {
628
+					$subStartPos = $pos;
629
+				}
630
+
631
+				if (null === $extraLeft && null !== $extraRight) {
632
+					// If inserting on the right only, skipping whitespace looks better
633
+					$subStartPos = $this->origTokens->skipRightWhitespace($subStartPos);
634
+				}
635
+				$subEndPos = $subStartPos - 1;
636
+			}
637
+
638
+			if (null === $subNode) {
639
+				// A node has been removed, check if we have removal information for it
640
+				$key = $type . '->' . $subNodeName;
641
+				if (!isset($this->removalMap[$key])) {
642
+					return $this->pFallback($fallbackNode);
643
+				}
644
+
645
+				// Adjust positions to account for additional tokens that must be skipped
646
+				$removalInfo = $this->removalMap[$key];
647
+				if (isset($removalInfo['left'])) {
648
+					$subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1;
649
+				}
650
+				if (isset($removalInfo['right'])) {
651
+					$subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1;
652
+				}
653
+			}
654
+
655
+			$result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment);
656
+
657
+			if (null !== $subNode) {
658
+				$result .= $extraLeft;
659
+
660
+				$origIndentLevel = $this->indentLevel;
661
+				$this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
662
+
663
+				// If it's the same node that was previously in this position, it certainly doesn't
664
+				// need fixup. It's important to check this here, because our fixup checks are more
665
+				// conservative than strictly necessary.
666
+				if (isset($fixupInfo[$subNodeName])
667
+					&& $subNode->getAttribute('origNode') !== $origSubNode
668
+				) {
669
+					$fixup = $fixupInfo[$subNodeName];
670
+					$res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos);
671
+				} else {
672
+					$res = $this->p($subNode, true);
673
+				}
674
+
675
+				$this->safeAppend($result, $res);
676
+				$this->setIndentLevel($origIndentLevel);
677
+
678
+				$result .= $extraRight;
679
+			}
680
+
681
+			$pos = $subEndPos + 1;
682
+		}
683
+
684
+		$result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment);
685
+		return $result;
686
+	}
687
+
688
+	/**
689
+	 * Perform a format-preserving pretty print of an array.
690
+	 *
691
+	 * @param array       $nodes            New nodes
692
+	 * @param array       $origNodes        Original nodes
693
+	 * @param int         $pos              Current token position (updated by reference)
694
+	 * @param int         $indentAdjustment Adjustment for indentation
695
+	 * @param string      $parentNodeType   Type of the containing node.
696
+	 * @param string      $subNodeName      Name of array subnode.
697
+	 * @param null|int    $fixup            Fixup information for array item nodes
698
+	 *
699
+	 * @return null|string Result of pretty print or null if cannot preserve formatting
700
+	 */
701
+	protected function pArray(
702
+		array $nodes, array $origNodes, int &$pos, int $indentAdjustment,
703
+		string $parentNodeType, string $subNodeName, $fixup
704
+	) {
705
+		$diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);
706
+
707
+		$mapKey = $parentNodeType . '->' . $subNodeName;
708
+		$insertStr = $this->listInsertionMap[$mapKey] ?? null;
709
+		$isStmtList = $subNodeName === 'stmts';
710
+
711
+		$beforeFirstKeepOrReplace = true;
712
+		$skipRemovedNode = false;
713
+		$delayedAdd = [];
714
+		$lastElemIndentLevel = $this->indentLevel;
715
+
716
+		$insertNewline = false;
717
+		if ($insertStr === "\n") {
718
+			$insertStr = '';
719
+			$insertNewline = true;
720
+		}
721
+
722
+		if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
723
+			$startPos = $origNodes[0]->getStartTokenPos();
724
+			$endPos = $origNodes[0]->getEndTokenPos();
725
+			\assert($startPos >= 0 && $endPos >= 0);
726
+			if (!$this->origTokens->haveBraces($startPos, $endPos)) {
727
+				// This was a single statement without braces, but either additional statements
728
+				// have been added, or the single statement has been removed. This requires the
729
+				// addition of braces. For now fall back.
730
+				// TODO: Try to preserve formatting
731
+				return null;
732
+			}
733
+		}
734
+
735
+		$result = '';
736
+		foreach ($diff as $i => $diffElem) {
737
+			$diffType = $diffElem->type;
738
+			/** @var Node|null $arrItem */
739
+			$arrItem = $diffElem->new;
740
+			/** @var Node|null $origArrItem */
741
+			$origArrItem = $diffElem->old;
742
+
743
+			if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
744
+				$beforeFirstKeepOrReplace = false;
745
+
746
+				if ($origArrItem === null || $arrItem === null) {
747
+					// We can only handle the case where both are null
748
+					if ($origArrItem === $arrItem) {
749
+						continue;
750
+					}
751
+					return null;
752
+				}
753
+
754
+				if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
755
+					// We can only deal with nodes. This can occur for Names, which use string arrays.
756
+					return null;
757
+				}
758
+
759
+				$itemStartPos = $origArrItem->getStartTokenPos();
760
+				$itemEndPos = $origArrItem->getEndTokenPos();
761
+				\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
762
+
763
+				$origIndentLevel = $this->indentLevel;
764
+				$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
765
+				$this->setIndentLevel($lastElemIndentLevel);
766
+
767
+				$comments = $arrItem->getComments();
768
+				$origComments = $origArrItem->getComments();
769
+				$commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
770
+				\assert($commentStartPos >= 0);
771
+
772
+				if ($commentStartPos < $pos) {
773
+					// Comments may be assigned to multiple nodes if they start at the same position.
774
+					// Make sure we don't try to print them multiple times.
775
+					$commentStartPos = $itemStartPos;
776
+				}
777
+
778
+				if ($skipRemovedNode) {
779
+					if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
780
+										$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
781
+						// We'd remove the brace of a code block.
782
+						// TODO: Preserve formatting.
783
+						$this->setIndentLevel($origIndentLevel);
784
+						return null;
785
+					}
786
+				} else {
787
+					$result .= $this->origTokens->getTokenCode(
788
+						$pos, $commentStartPos, $indentAdjustment);
789
+				}
790
+
791
+				if (!empty($delayedAdd)) {
792
+					/** @var Node $delayedAddNode */
793
+					foreach ($delayedAdd as $delayedAddNode) {
794
+						if ($insertNewline) {
795
+							$delayedAddComments = $delayedAddNode->getComments();
796
+							if ($delayedAddComments) {
797
+								$result .= $this->pComments($delayedAddComments) . $this->nl;
798
+							}
799
+						}
800
+
801
+						$this->safeAppend($result, $this->p($delayedAddNode, true));
802
+
803
+						if ($insertNewline) {
804
+							$result .= $insertStr . $this->nl;
805
+						} else {
806
+							$result .= $insertStr;
807
+						}
808
+					}
809
+
810
+					$delayedAdd = [];
811
+				}
812
+
813
+				if ($comments !== $origComments) {
814
+					if ($comments) {
815
+						$result .= $this->pComments($comments) . $this->nl;
816
+					}
817
+				} else {
818
+					$result .= $this->origTokens->getTokenCode(
819
+						$commentStartPos, $itemStartPos, $indentAdjustment);
820
+				}
821
+
822
+				// If we had to remove anything, we have done so now.
823
+				$skipRemovedNode = false;
824
+			} elseif ($diffType === DiffElem::TYPE_ADD) {
825
+				if (null === $insertStr) {
826
+					// We don't have insertion information for this list type
827
+					return null;
828
+				}
829
+
830
+				// We go multiline if the original code was multiline,
831
+				// or if it's an array item with a comment above it.
832
+				if ($insertStr === ', ' &&
833
+					($this->isMultiline($origNodes) || $arrItem->getComments())
834
+				) {
835
+					$insertStr = ',';
836
+					$insertNewline = true;
837
+				}
838
+
839
+				if ($beforeFirstKeepOrReplace) {
840
+					// Will be inserted at the next "replace" or "keep" element
841
+					$delayedAdd[] = $arrItem;
842
+					continue;
843
+				}
844
+
845
+				$itemStartPos = $pos;
846
+				$itemEndPos = $pos - 1;
847
+
848
+				$origIndentLevel = $this->indentLevel;
849
+				$this->setIndentLevel($lastElemIndentLevel);
850
+
851
+				if ($insertNewline) {
852
+					$result .= $insertStr . $this->nl;
853
+					$comments = $arrItem->getComments();
854
+					if ($comments) {
855
+						$result .= $this->pComments($comments) . $this->nl;
856
+					}
857
+				} else {
858
+					$result .= $insertStr;
859
+				}
860
+			} elseif ($diffType === DiffElem::TYPE_REMOVE) {
861
+				if (!$origArrItem instanceof Node) {
862
+					// We only support removal for nodes
863
+					return null;
864
+				}
865
+
866
+				$itemStartPos = $origArrItem->getStartTokenPos();
867
+				$itemEndPos = $origArrItem->getEndTokenPos();
868
+				\assert($itemStartPos >= 0 && $itemEndPos >= 0);
869
+
870
+				// Consider comments part of the node.
871
+				$origComments = $origArrItem->getComments();
872
+				if ($origComments) {
873
+					$itemStartPos = $origComments[0]->getStartTokenPos();
874
+				}
875
+
876
+				if ($i === 0) {
877
+					// If we're removing from the start, keep the tokens before the node and drop those after it,
878
+					// instead of the other way around.
879
+					$result .= $this->origTokens->getTokenCode(
880
+						$pos, $itemStartPos, $indentAdjustment);
881
+					$skipRemovedNode = true;
882
+				} else {
883
+					if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
884
+										$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
885
+						// We'd remove the brace of a code block.
886
+						// TODO: Preserve formatting.
887
+						return null;
888
+					}
889
+				}
890
+
891
+				$pos = $itemEndPos + 1;
892
+				continue;
893
+			} else {
894
+				throw new \Exception("Shouldn't happen");
895
+			}
896
+
897
+			if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
898
+				$res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
899
+			} else {
900
+				$res = $this->p($arrItem, true);
901
+			}
902
+			$this->safeAppend($result, $res);
903
+
904
+			$this->setIndentLevel($origIndentLevel);
905
+			$pos = $itemEndPos + 1;
906
+		}
907
+
908
+		if ($skipRemovedNode) {
909
+			// TODO: Support removing single node.
910
+			return null;
911
+		}
912
+
913
+		if (!empty($delayedAdd)) {
914
+			if (!isset($this->emptyListInsertionMap[$mapKey])) {
915
+				return null;
916
+			}
917
+
918
+			list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
919
+			if (null !== $findToken) {
920
+				$insertPos = $this->origTokens->findRight($pos, $findToken) + 1;
921
+				$result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
922
+				$pos = $insertPos;
923
+			}
924
+
925
+			$first = true;
926
+			$result .= $extraLeft;
927
+			foreach ($delayedAdd as $delayedAddNode) {
928
+				if (!$first) {
929
+					$result .= $insertStr;
930
+					if ($insertNewline) {
931
+						$result .= $this->nl;
932
+					}
933
+				}
934
+				$result .= $this->p($delayedAddNode, true);
935
+				$first = false;
936
+			}
937
+			$result .= $extraRight === "\n" ? $this->nl : $extraRight;
938
+		}
939
+
940
+		return $result;
941
+	}
942
+
943
+	/**
944
+	 * Print node with fixups.
945
+	 *
946
+	 * Fixups here refer to the addition of extra parentheses, braces or other characters, that
947
+	 * are required to preserve program semantics in a certain context (e.g. to maintain precedence
948
+	 * or because only certain expressions are allowed in certain places).
949
+	 *
950
+	 * @param int         $fixup       Fixup type
951
+	 * @param Node        $subNode     Subnode to print
952
+	 * @param string|null $parentClass Class of parent node
953
+	 * @param int         $subStartPos Original start pos of subnode
954
+	 * @param int         $subEndPos   Original end pos of subnode
955
+	 *
956
+	 * @return string Result of fixed-up print of subnode
957
+	 */
958
+	protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string {
959
+		switch ($fixup) {
960
+			case self::FIXUP_PREC_LEFT:
961
+			case self::FIXUP_PREC_RIGHT:
962
+				if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
963
+					list($precedence, $associativity) = $this->precedenceMap[$parentClass];
964
+					return $this->pPrec($subNode, $precedence, $associativity,
965
+						$fixup === self::FIXUP_PREC_LEFT ? -1 : 1);
966
+				}
967
+				break;
968
+			case self::FIXUP_CALL_LHS:
969
+				if ($this->callLhsRequiresParens($subNode)
970
+					&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
971
+				) {
972
+					return '(' . $this->p($subNode) . ')';
973
+				}
974
+				break;
975
+			case self::FIXUP_DEREF_LHS:
976
+				if ($this->dereferenceLhsRequiresParens($subNode)
977
+					&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
978
+				) {
979
+					return '(' . $this->p($subNode) . ')';
980
+				}
981
+				break;
982
+			case self::FIXUP_STATIC_DEREF_LHS:
983
+				if ($this->staticDereferenceLhsRequiresParens($subNode)
984
+					&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
985
+				) {
986
+					return '(' . $this->p($subNode) . ')';
987
+				}
988
+				break;
989
+			case self::FIXUP_NEW:
990
+				if ($this->newOperandRequiresParens($subNode)
991
+					&& !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
992
+					return '(' . $this->p($subNode) . ')';
993
+				}
994
+				break;
995
+			case self::FIXUP_BRACED_NAME:
996
+			case self::FIXUP_VAR_BRACED_NAME:
997
+				if ($subNode instanceof Expr
998
+					&& !$this->origTokens->haveBraces($subStartPos, $subEndPos)
999
+				) {
1000
+					return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
1001
+						. '{' . $this->p($subNode) . '}';
1002
+				}
1003
+				break;
1004
+			case self::FIXUP_ENCAPSED:
1005
+				if (!$subNode instanceof Scalar\EncapsedStringPart
1006
+					&& !$this->origTokens->haveBraces($subStartPos, $subEndPos)
1007
+				) {
1008
+					return '{' . $this->p($subNode) . '}';
1009
+				}
1010
+				break;
1011
+			default:
1012
+				throw new \Exception('Cannot happen');
1013
+		}
1014
+
1015
+		// Nothing special to do
1016
+		return $this->p($subNode);
1017
+	}
1018
+
1019
+	/**
1020
+	 * Appends to a string, ensuring whitespace between label characters.
1021
+	 *
1022
+	 * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x".
1023
+	 * Without safeAppend the result would be "echox", which does not preserve semantics.
1024
+	 *
1025
+	 * @param string $str
1026
+	 * @param string $append
1027
+	 */
1028
+	protected function safeAppend(string &$str, string $append) {
1029
+		if ($str === "") {
1030
+			$str = $append;
1031
+			return;
1032
+		}
1033
+
1034
+		if ($append === "") {
1035
+			return;
1036
+		}
1037
+
1038
+		if (!$this->labelCharMap[$append[0]]
1039
+				|| !$this->labelCharMap[$str[\strlen($str) - 1]]) {
1040
+			$str .= $append;
1041
+		} else {
1042
+			$str .= " " . $append;
1043
+		}
1044
+	}
1045
+
1046
+	/**
1047
+	 * Determines whether the LHS of a call must be wrapped in parenthesis.
1048
+	 *
1049
+	 * @param Node $node LHS of a call
1050
+	 *
1051
+	 * @return bool Whether parentheses are required
1052
+	 */
1053
+	protected function callLhsRequiresParens(Node $node) : bool {
1054
+		return !($node instanceof Node\Name
1055
+			|| $node instanceof Expr\Variable
1056
+			|| $node instanceof Expr\ArrayDimFetch
1057
+			|| $node instanceof Expr\FuncCall
1058
+			|| $node instanceof Expr\MethodCall
1059
+			|| $node instanceof Expr\NullsafeMethodCall
1060
+			|| $node instanceof Expr\StaticCall
1061
+			|| $node instanceof Expr\Array_);
1062
+	}
1063
+
1064
+	/**
1065
+	 * Determines whether the LHS of an array/object operation must be wrapped in parentheses.
1066
+	 *
1067
+	 * @param Node $node LHS of dereferencing operation
1068
+	 *
1069
+	 * @return bool Whether parentheses are required
1070
+	 */
1071
+	protected function dereferenceLhsRequiresParens(Node $node) : bool {
1072
+		// A constant can occur on the LHS of an array/object deref, but not a static deref.
1073
+		return $this->staticDereferenceLhsRequiresParens($node)
1074
+			&& !$node instanceof Expr\ConstFetch;
1075
+	}
1076
+
1077
+	/**
1078
+	 * Determines whether the LHS of a static operation must be wrapped in parentheses.
1079
+	 *
1080
+	 * @param Node $node LHS of dereferencing operation
1081
+	 *
1082
+	 * @return bool Whether parentheses are required
1083
+	 */
1084
+	protected function staticDereferenceLhsRequiresParens(Node $node): bool {
1085
+		return !($node instanceof Expr\Variable
1086
+			|| $node instanceof Node\Name
1087
+			|| $node instanceof Expr\ArrayDimFetch
1088
+			|| $node instanceof Expr\PropertyFetch
1089
+			|| $node instanceof Expr\NullsafePropertyFetch
1090
+			|| $node instanceof Expr\StaticPropertyFetch
1091
+			|| $node instanceof Expr\FuncCall
1092
+			|| $node instanceof Expr\MethodCall
1093
+			|| $node instanceof Expr\NullsafeMethodCall
1094
+			|| $node instanceof Expr\StaticCall
1095
+			|| $node instanceof Expr\Array_
1096
+			|| $node instanceof Scalar\String_
1097
+			|| $node instanceof Expr\ClassConstFetch);
1098
+	}
1099
+
1100
+	/**
1101
+	 * Determines whether an expression used in "new" or "instanceof" requires parentheses.
1102
+	 *
1103
+	 * @param Node $node New or instanceof operand
1104
+	 *
1105
+	 * @return bool Whether parentheses are required
1106
+	 */
1107
+	protected function newOperandRequiresParens(Node $node): bool {
1108
+		if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
1109
+			return false;
1110
+		}
1111
+		if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
1112
+			$node instanceof Expr\NullsafePropertyFetch
1113
+		) {
1114
+			return $this->newOperandRequiresParens($node->var);
1115
+		}
1116
+		if ($node instanceof Expr\StaticPropertyFetch) {
1117
+			return $this->newOperandRequiresParens($node->class);
1118
+		}
1119
+		return true;
1120
+	}
1121
+
1122
+	/**
1123
+	 * Print modifiers, including trailing whitespace.
1124
+	 *
1125
+	 * @param int $modifiers Modifier mask to print
1126
+	 *
1127
+	 * @return string Printed modifiers
1128
+	 */
1129
+	protected function pModifiers(int $modifiers) {
1130
+		return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC    ? 'public '    : '')
1131
+			 . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
1132
+			 . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE   ? 'private '   : '')
1133
+			 . ($modifiers & Stmt\Class_::MODIFIER_STATIC    ? 'static '    : '')
1134
+			 . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT  ? 'abstract '  : '')
1135
+			 . ($modifiers & Stmt\Class_::MODIFIER_FINAL     ? 'final '     : '')
1136
+			 . ($modifiers & Stmt\Class_::MODIFIER_READONLY  ? 'readonly '  : '');
1137
+	}
1138
+
1139
+	/**
1140
+	 * Determine whether a list of nodes uses multiline formatting.
1141
+	 *
1142
+	 * @param (Node|null)[] $nodes Node list
1143
+	 *
1144
+	 * @return bool Whether multiline formatting is used
1145
+	 */
1146
+	protected function isMultiline(array $nodes) : bool {
1147
+		if (\count($nodes) < 2) {
1148
+			return false;
1149
+		}
1150
+
1151
+		$pos = -1;
1152
+		foreach ($nodes as $node) {
1153
+			if (null === $node) {
1154
+				continue;
1155
+			}
1156
+
1157
+			$endPos = $node->getEndTokenPos() + 1;
1158
+			if ($pos >= 0) {
1159
+				$text = $this->origTokens->getTokenCode($pos, $endPos, 0);
1160
+				if (false === strpos($text, "\n")) {
1161
+					// We require that a newline is present between *every* item. If the formatting
1162
+					// is inconsistent, with only some items having newlines, we don't consider it
1163
+					// as multiline
1164
+					return false;
1165
+				}
1166
+			}
1167
+			$pos = $endPos;
1168
+		}
1169
+
1170
+		return true;
1171
+	}
1172
+
1173
+	/**
1174
+	 * Lazily initializes label char map.
1175
+	 *
1176
+	 * The label char map determines whether a certain character may occur in a label.
1177
+	 */
1178
+	protected function initializeLabelCharMap() {
1179
+		if ($this->labelCharMap) return;
1180
+
1181
+		$this->labelCharMap = [];
1182
+		for ($i = 0; $i < 256; $i++) {
1183
+			// Since PHP 7.1 The lower range is 0x80. However, we also want to support code for
1184
+			// older versions.
1185
+			$chr = chr($i);
1186
+			$this->labelCharMap[$chr] = $i >= 0x7f || ctype_alnum($chr);
1187
+		}
1188
+	}
1189
+
1190
+	/**
1191
+	 * Lazily initializes node list differ.
1192
+	 *
1193
+	 * The node list differ is used to determine differences between two array subnodes.
1194
+	 */
1195
+	protected function initializeNodeListDiffer() {
1196
+		if ($this->nodeListDiffer) return;
1197
+
1198
+		$this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
1199
+			if ($a instanceof Node && $b instanceof Node) {
1200
+				return $a === $b->getAttribute('origNode');
1201
+			}
1202
+			// Can happen for array destructuring
1203
+			return $a === null && $b === null;
1204
+		});
1205
+	}
1206
+
1207
+	/**
1208
+	 * Lazily initializes fixup map.
1209
+	 *
1210
+	 * The fixup map is used to determine whether a certain subnode of a certain node may require
1211
+	 * some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
1212
+	 */
1213
+	protected function initializeFixupMap() {
1214
+		if ($this->fixupMap) return;
1215
+
1216
+		$this->fixupMap = [
1217
+			Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT],
1218
+			Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT],
1219
+			Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT],
1220
+			Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT],
1221
+			Expr\Instanceof_::class => [
1222
+				'expr' => self::FIXUP_PREC_LEFT,
1223
+				'class' => self::FIXUP_NEW,
1224
+			],
1225
+			Expr\Ternary::class => [
1226
+				'cond' => self::FIXUP_PREC_LEFT,
1227
+				'else' => self::FIXUP_PREC_RIGHT,
1228
+			],
1229
+
1230
+			Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
1231
+			Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
1232
+			Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
1233
+			Expr\ClassConstFetch::class => [
1234
+				'class' => self::FIXUP_STATIC_DEREF_LHS,
1235
+				'name' => self::FIXUP_BRACED_NAME,
1236
+			],
1237
+			Expr\New_::class => ['class' => self::FIXUP_NEW],
1238
+			Expr\MethodCall::class => [
1239
+				'var' => self::FIXUP_DEREF_LHS,
1240
+				'name' => self::FIXUP_BRACED_NAME,
1241
+			],
1242
+			Expr\NullsafeMethodCall::class => [
1243
+				'var' => self::FIXUP_DEREF_LHS,
1244
+				'name' => self::FIXUP_BRACED_NAME,
1245
+			],
1246
+			Expr\StaticPropertyFetch::class => [
1247
+				'class' => self::FIXUP_STATIC_DEREF_LHS,
1248
+				'name' => self::FIXUP_VAR_BRACED_NAME,
1249
+			],
1250
+			Expr\PropertyFetch::class => [
1251
+				'var' => self::FIXUP_DEREF_LHS,
1252
+				'name' => self::FIXUP_BRACED_NAME,
1253
+			],
1254
+			Expr\NullsafePropertyFetch::class => [
1255
+				'var' => self::FIXUP_DEREF_LHS,
1256
+				'name' => self::FIXUP_BRACED_NAME,
1257
+			],
1258
+			Scalar\Encapsed::class => [
1259
+				'parts' => self::FIXUP_ENCAPSED,
1260
+			],
1261
+		];
1262
+
1263
+		$binaryOps = [
1264
+			BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class,
1265
+			BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class,
1266
+			BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class,
1267
+			BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class,
1268
+			BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class,
1269
+			BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
1270
+			BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
1271
+			BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
1272
+			BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class,
1273
+		];
1274
+		foreach ($binaryOps as $binaryOp) {
1275
+			$this->fixupMap[$binaryOp] = [
1276
+				'left' => self::FIXUP_PREC_LEFT,
1277
+				'right' => self::FIXUP_PREC_RIGHT
1278
+			];
1279
+		}
1280
+
1281
+		$assignOps = [
1282
+			Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
1283
+			AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
1284
+			AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
1285
+			AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class
1286
+		];
1287
+		foreach ($assignOps as $assignOp) {
1288
+			$this->fixupMap[$assignOp] = [
1289
+				'var' => self::FIXUP_PREC_LEFT,
1290
+				'expr' => self::FIXUP_PREC_RIGHT,
1291
+			];
1292
+		}
1293
+
1294
+		$prefixOps = [
1295
+			Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class,
1296
+			Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class,
1297
+			Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class,
1298
+			Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class,
1299
+		];
1300
+		foreach ($prefixOps as $prefixOp) {
1301
+			$this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT];
1302
+		}
1303
+	}
1304
+
1305
+	/**
1306
+	 * Lazily initializes the removal map.
1307
+	 *
1308
+	 * The removal map is used to determine which additional tokens should be removed when a
1309
+	 * certain node is replaced by null.
1310
+	 */
1311
+	protected function initializeRemovalMap() {
1312
+		if ($this->removalMap) return;
1313
+
1314
+		$stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
1315
+		$stripLeft = ['left' => \T_WHITESPACE];
1316
+		$stripRight = ['right' => \T_WHITESPACE];
1317
+		$stripDoubleArrow = ['right' => \T_DOUBLE_ARROW];
1318
+		$stripColon = ['left' => ':'];
1319
+		$stripEquals = ['left' => '='];
1320
+		$this->removalMap = [
1321
+			'Expr_ArrayDimFetch->dim' => $stripBoth,
1322
+			'Expr_ArrayItem->key' => $stripDoubleArrow,
1323
+			'Expr_ArrowFunction->returnType' => $stripColon,
1324
+			'Expr_Closure->returnType' => $stripColon,
1325
+			'Expr_Exit->expr' => $stripBoth,
1326
+			'Expr_Ternary->if' => $stripBoth,
1327
+			'Expr_Yield->key' => $stripDoubleArrow,
1328
+			'Expr_Yield->value' => $stripBoth,
1329
+			'Param->type' => $stripRight,
1330
+			'Param->default' => $stripEquals,
1331
+			'Stmt_Break->num' => $stripBoth,
1332
+			'Stmt_Catch->var' => $stripLeft,
1333
+			'Stmt_ClassConst->type' => $stripRight,
1334
+			'Stmt_ClassMethod->returnType' => $stripColon,
1335
+			'Stmt_Class->extends' => ['left' => \T_EXTENDS],
1336
+			'Stmt_Enum->scalarType' => $stripColon,
1337
+			'Stmt_EnumCase->expr' => $stripEquals,
1338
+			'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
1339
+			'Stmt_Continue->num' => $stripBoth,
1340
+			'Stmt_Foreach->keyVar' => $stripDoubleArrow,
1341
+			'Stmt_Function->returnType' => $stripColon,
1342
+			'Stmt_If->else' => $stripLeft,
1343
+			'Stmt_Namespace->name' => $stripLeft,
1344
+			'Stmt_Property->type' => $stripRight,
1345
+			'Stmt_PropertyProperty->default' => $stripEquals,
1346
+			'Stmt_Return->expr' => $stripBoth,
1347
+			'Stmt_StaticVar->default' => $stripEquals,
1348
+			'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft,
1349
+			'Stmt_TryCatch->finally' => $stripLeft,
1350
+			// 'Stmt_Case->cond': Replace with "default"
1351
+			// 'Stmt_Class->name': Unclear what to do
1352
+			// 'Stmt_Declare->stmts': Not a plain node
1353
+			// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
1354
+		];
1355
+	}
1356
+
1357
+	protected function initializeInsertionMap() {
1358
+		if ($this->insertionMap) return;
1359
+
1360
+		// TODO: "yield" where both key and value are inserted doesn't work
1361
+		// [$find, $beforeToken, $extraLeft, $extraRight]
1362
+		$this->insertionMap = [
1363
+			'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
1364
+			'Expr_ArrayItem->key' => [null, false, null, ' => '],
1365
+			'Expr_ArrowFunction->returnType' => [')', false, ' : ', null],
1366
+			'Expr_Closure->returnType' => [')', false, ' : ', null],
1367
+			'Expr_Ternary->if' => ['?', false, ' ', ' '],
1368
+			'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
1369
+			'Expr_Yield->value' => [\T_YIELD, false, ' ', null],
1370
+			'Param->type' => [null, false, null, ' '],
1371
+			'Param->default' => [null, false, ' = ', null],
1372
+			'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
1373
+			'Stmt_Catch->var' => [null, false, ' ', null],
1374
+			'Stmt_ClassMethod->returnType' => [')', false, ' : ', null],
1375
+			'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
1376
+			'Stmt_Class->extends' => [null, false, ' extends ', null],
1377
+			'Stmt_Enum->scalarType' => [null, false, ' : ', null],
1378
+			'Stmt_EnumCase->expr' => [null, false, ' = ', null],
1379
+			'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null],
1380
+			'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
1381
+			'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
1382
+			'Stmt_Function->returnType' => [')', false, ' : ', null],
1383
+			'Stmt_If->else' => [null, false, ' ', null],
1384
+			'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null],
1385
+			'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '],
1386
+			'Stmt_PropertyProperty->default' => [null, false, ' = ', null],
1387
+			'Stmt_Return->expr' => [\T_RETURN, false, ' ', null],
1388
+			'Stmt_StaticVar->default' => [null, false, ' = ', null],
1389
+			//'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO
1390
+			'Stmt_TryCatch->finally' => [null, false, ' ', null],
1391
+
1392
+			// 'Expr_Exit->expr': Complicated due to optional ()
1393
+			// 'Stmt_Case->cond': Conversion from default to case
1394
+			// 'Stmt_Class->name': Unclear
1395
+			// 'Stmt_Declare->stmts': Not a proper node
1396
+			// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
1397
+		];
1398
+	}
1399
+
1400
+	protected function initializeListInsertionMap() {
1401
+		if ($this->listInsertionMap) return;
1402
+
1403
+		$this->listInsertionMap = [
1404
+			// special
1405
+			//'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
1406
+			//'Scalar_Encapsed->parts' => '',
1407
+			'Stmt_Catch->types' => '|',
1408
+			'UnionType->types' => '|',
1409
+			'IntersectionType->types' => '&',
1410
+			'Stmt_If->elseifs' => ' ',
1411
+			'Stmt_TryCatch->catches' => ' ',
1412
+
1413
+			// comma-separated lists
1414
+			'Expr_Array->items' => ', ',
1415
+			'Expr_ArrowFunction->params' => ', ',
1416
+			'Expr_Closure->params' => ', ',
1417
+			'Expr_Closure->uses' => ', ',
1418
+			'Expr_FuncCall->args' => ', ',
1419
+			'Expr_Isset->vars' => ', ',
1420
+			'Expr_List->items' => ', ',
1421
+			'Expr_MethodCall->args' => ', ',
1422
+			'Expr_NullsafeMethodCall->args' => ', ',
1423
+			'Expr_New->args' => ', ',
1424
+			'Expr_PrintableNewAnonClass->args' => ', ',
1425
+			'Expr_StaticCall->args' => ', ',
1426
+			'Stmt_ClassConst->consts' => ', ',
1427
+			'Stmt_ClassMethod->params' => ', ',
1428
+			'Stmt_Class->implements' => ', ',
1429
+			'Stmt_Enum->implements' => ', ',
1430
+			'Expr_PrintableNewAnonClass->implements' => ', ',
1431
+			'Stmt_Const->consts' => ', ',
1432
+			'Stmt_Declare->declares' => ', ',
1433
+			'Stmt_Echo->exprs' => ', ',
1434
+			'Stmt_For->init' => ', ',
1435
+			'Stmt_For->cond' => ', ',
1436
+			'Stmt_For->loop' => ', ',
1437
+			'Stmt_Function->params' => ', ',
1438
+			'Stmt_Global->vars' => ', ',
1439
+			'Stmt_GroupUse->uses' => ', ',
1440
+			'Stmt_Interface->extends' => ', ',
1441
+			'Stmt_Match->arms' => ', ',
1442
+			'Stmt_Property->props' => ', ',
1443
+			'Stmt_StaticVar->vars' => ', ',
1444
+			'Stmt_TraitUse->traits' => ', ',
1445
+			'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ',
1446
+			'Stmt_Unset->vars' => ', ',
1447
+			'Stmt_Use->uses' => ', ',
1448
+			'MatchArm->conds' => ', ',
1449
+			'AttributeGroup->attrs' => ', ',
1450
+
1451
+			// statement lists
1452
+			'Expr_Closure->stmts' => "\n",
1453
+			'Stmt_Case->stmts' => "\n",
1454
+			'Stmt_Catch->stmts' => "\n",
1455
+			'Stmt_Class->stmts' => "\n",
1456
+			'Stmt_Enum->stmts' => "\n",
1457
+			'Expr_PrintableNewAnonClass->stmts' => "\n",
1458
+			'Stmt_Interface->stmts' => "\n",
1459
+			'Stmt_Trait->stmts' => "\n",
1460
+			'Stmt_ClassMethod->stmts' => "\n",
1461
+			'Stmt_Declare->stmts' => "\n",
1462
+			'Stmt_Do->stmts' => "\n",
1463
+			'Stmt_ElseIf->stmts' => "\n",
1464
+			'Stmt_Else->stmts' => "\n",
1465
+			'Stmt_Finally->stmts' => "\n",
1466
+			'Stmt_Foreach->stmts' => "\n",
1467
+			'Stmt_For->stmts' => "\n",
1468
+			'Stmt_Function->stmts' => "\n",
1469
+			'Stmt_If->stmts' => "\n",
1470
+			'Stmt_Namespace->stmts' => "\n",
1471
+			'Stmt_Class->attrGroups' => "\n",
1472
+			'Stmt_Enum->attrGroups' => "\n",
1473
+			'Stmt_EnumCase->attrGroups' => "\n",
1474
+			'Stmt_Interface->attrGroups' => "\n",
1475
+			'Stmt_Trait->attrGroups' => "\n",
1476
+			'Stmt_Function->attrGroups' => "\n",
1477
+			'Stmt_ClassMethod->attrGroups' => "\n",
1478
+			'Stmt_ClassConst->attrGroups' => "\n",
1479
+			'Stmt_Property->attrGroups' => "\n",
1480
+			'Expr_PrintableNewAnonClass->attrGroups' => ' ',
1481
+			'Expr_Closure->attrGroups' => ' ',
1482
+			'Expr_ArrowFunction->attrGroups' => ' ',
1483
+			'Param->attrGroups' => ' ',
1484
+			'Stmt_Switch->cases' => "\n",
1485
+			'Stmt_TraitUse->adaptations' => "\n",
1486
+			'Stmt_TryCatch->stmts' => "\n",
1487
+			'Stmt_While->stmts' => "\n",
1488
+
1489
+			// dummy for top-level context
1490
+			'File->stmts' => "\n",
1491
+		];
1492
+	}
1493
+
1494
+	protected function initializeEmptyListInsertionMap() {
1495
+		if ($this->emptyListInsertionMap) return;
1496
+
1497
+		// TODO Insertion into empty statement lists.
1498
+
1499
+		// [$find, $extraLeft, $extraRight]
1500
+		$this->emptyListInsertionMap = [
1501
+			'Expr_ArrowFunction->params' => ['(', '', ''],
1502
+			'Expr_Closure->uses' => [')', ' use(', ')'],
1503
+			'Expr_Closure->params' => ['(', '', ''],
1504
+			'Expr_FuncCall->args' => ['(', '', ''],
1505
+			'Expr_MethodCall->args' => ['(', '', ''],
1506
+			'Expr_NullsafeMethodCall->args' => ['(', '', ''],
1507
+			'Expr_New->args' => ['(', '', ''],
1508
+			'Expr_PrintableNewAnonClass->args' => ['(', '', ''],
1509
+			'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''],
1510
+			'Expr_StaticCall->args' => ['(', '', ''],
1511
+			'Stmt_Class->implements' => [null, ' implements ', ''],
1512
+			'Stmt_Enum->implements' => [null, ' implements ', ''],
1513
+			'Stmt_ClassMethod->params' => ['(', '', ''],
1514
+			'Stmt_Interface->extends' => [null, ' extends ', ''],
1515
+			'Stmt_Function->params' => ['(', '', ''],
1516
+			'Stmt_Interface->attrGroups' => [null, '', "\n"],
1517
+			'Stmt_Class->attrGroups' => [null, '', "\n"],
1518
+			'Stmt_ClassConst->attrGroups' => [null, '', "\n"],
1519
+			'Stmt_ClassMethod->attrGroups' => [null, '', "\n"],
1520
+			'Stmt_Function->attrGroups' => [null, '', "\n"],
1521
+			'Stmt_Property->attrGroups' => [null, '', "\n"],
1522
+			'Stmt_Trait->attrGroups' => [null, '', "\n"],
1523
+			'Expr_ArrowFunction->attrGroups' => [null, '', ' '],
1524
+			'Expr_Closure->attrGroups' => [null, '', ' '],
1525
+			'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', ''],
1526
+
1527
+			/* These cannot be empty to start with:
1528 1528
              * Expr_Isset->vars
1529 1529
              * Stmt_Catch->types
1530 1530
              * Stmt_Const->consts
@@ -1542,7 +1542,7 @@  discard block
 block discarded – undo
1542 1542
              * UnionType->types
1543 1543
              */
1544 1544
 
1545
-            /* TODO
1545
+			/* TODO
1546 1546
              * Stmt_If->elseifs
1547 1547
              * Stmt_TryCatch->catches
1548 1548
              * Expr_Array->items
@@ -1551,26 +1551,26 @@  discard block
 block discarded – undo
1551 1551
              * Stmt_For->cond
1552 1552
              * Stmt_For->loop
1553 1553
              */
1554
-        ];
1555
-    }
1556
-
1557
-    protected function initializeModifierChangeMap() {
1558
-        if ($this->modifierChangeMap) return;
1559
-
1560
-        $this->modifierChangeMap = [
1561
-            'Stmt_ClassConst->flags' => \T_CONST,
1562
-            'Stmt_ClassMethod->flags' => \T_FUNCTION,
1563
-            'Stmt_Class->flags' => \T_CLASS,
1564
-            'Stmt_Property->flags' => \T_VARIABLE,
1565
-            'Expr_PrintableNewAnonClass->flags' => \T_CLASS,
1566
-            'Param->flags' => \T_VARIABLE,
1567
-            //'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
1568
-        ];
1569
-
1570
-        // List of integer subnodes that are not modifiers:
1571
-        // Expr_Include->type
1572
-        // Stmt_GroupUse->type
1573
-        // Stmt_Use->type
1574
-        // Stmt_UseUse->type
1575
-    }
1554
+		];
1555
+	}
1556
+
1557
+	protected function initializeModifierChangeMap() {
1558
+		if ($this->modifierChangeMap) return;
1559
+
1560
+		$this->modifierChangeMap = [
1561
+			'Stmt_ClassConst->flags' => \T_CONST,
1562
+			'Stmt_ClassMethod->flags' => \T_FUNCTION,
1563
+			'Stmt_Class->flags' => \T_CLASS,
1564
+			'Stmt_Property->flags' => \T_VARIABLE,
1565
+			'Expr_PrintableNewAnonClass->flags' => \T_CLASS,
1566
+			'Param->flags' => \T_VARIABLE,
1567
+			//'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO
1568
+		];
1569
+
1570
+		// List of integer subnodes that are not modifiers:
1571
+		// Expr_Include->type
1572
+		// Stmt_GroupUse->type
1573
+		// Stmt_Use->type
1574
+		// Stmt_UseUse->type
1575
+	}
1576 1576
 }
Please login to merge, or discard this patch.
Spacing   +94 added lines, -94 removed lines patch added patch discarded remove patch
@@ -27,66 +27,66 @@  discard block
 block discarded – undo
27 27
     protected $precedenceMap = [
28 28
         // [precedence, associativity]
29 29
         // where for precedence -1 is %left, 0 is %nonassoc and 1 is %right
30
-        BinaryOp\Pow::class            => [  0,  1],
31
-        Expr\BitwiseNot::class         => [ 10,  1],
32
-        Expr\PreInc::class             => [ 10,  1],
33
-        Expr\PreDec::class             => [ 10,  1],
34
-        Expr\PostInc::class            => [ 10, -1],
35
-        Expr\PostDec::class            => [ 10, -1],
36
-        Expr\UnaryPlus::class          => [ 10,  1],
37
-        Expr\UnaryMinus::class         => [ 10,  1],
38
-        Cast\Int_::class               => [ 10,  1],
39
-        Cast\Double::class             => [ 10,  1],
40
-        Cast\String_::class            => [ 10,  1],
41
-        Cast\Array_::class             => [ 10,  1],
42
-        Cast\Object_::class            => [ 10,  1],
43
-        Cast\Bool_::class              => [ 10,  1],
44
-        Cast\Unset_::class             => [ 10,  1],
45
-        Expr\ErrorSuppress::class      => [ 10,  1],
46
-        Expr\Instanceof_::class        => [ 20,  0],
47
-        Expr\BooleanNot::class         => [ 30,  1],
48
-        BinaryOp\Mul::class            => [ 40, -1],
49
-        BinaryOp\Div::class            => [ 40, -1],
50
-        BinaryOp\Mod::class            => [ 40, -1],
51
-        BinaryOp\Plus::class           => [ 50, -1],
52
-        BinaryOp\Minus::class          => [ 50, -1],
53
-        BinaryOp\Concat::class         => [ 50, -1],
54
-        BinaryOp\ShiftLeft::class      => [ 60, -1],
55
-        BinaryOp\ShiftRight::class     => [ 60, -1],
56
-        BinaryOp\Smaller::class        => [ 70,  0],
57
-        BinaryOp\SmallerOrEqual::class => [ 70,  0],
58
-        BinaryOp\Greater::class        => [ 70,  0],
59
-        BinaryOp\GreaterOrEqual::class => [ 70,  0],
60
-        BinaryOp\Equal::class          => [ 80,  0],
61
-        BinaryOp\NotEqual::class       => [ 80,  0],
62
-        BinaryOp\Identical::class      => [ 80,  0],
63
-        BinaryOp\NotIdentical::class   => [ 80,  0],
64
-        BinaryOp\Spaceship::class      => [ 80,  0],
65
-        BinaryOp\BitwiseAnd::class     => [ 90, -1],
30
+        BinaryOp\Pow::class            => [0, 1],
31
+        Expr\BitwiseNot::class         => [10, 1],
32
+        Expr\PreInc::class             => [10, 1],
33
+        Expr\PreDec::class             => [10, 1],
34
+        Expr\PostInc::class            => [10, -1],
35
+        Expr\PostDec::class            => [10, -1],
36
+        Expr\UnaryPlus::class          => [10, 1],
37
+        Expr\UnaryMinus::class         => [10, 1],
38
+        Cast\Int_::class               => [10, 1],
39
+        Cast\Double::class             => [10, 1],
40
+        Cast\String_::class            => [10, 1],
41
+        Cast\Array_::class             => [10, 1],
42
+        Cast\Object_::class            => [10, 1],
43
+        Cast\Bool_::class              => [10, 1],
44
+        Cast\Unset_::class             => [10, 1],
45
+        Expr\ErrorSuppress::class      => [10, 1],
46
+        Expr\Instanceof_::class        => [20, 0],
47
+        Expr\BooleanNot::class         => [30, 1],
48
+        BinaryOp\Mul::class            => [40, -1],
49
+        BinaryOp\Div::class            => [40, -1],
50
+        BinaryOp\Mod::class            => [40, -1],
51
+        BinaryOp\Plus::class           => [50, -1],
52
+        BinaryOp\Minus::class          => [50, -1],
53
+        BinaryOp\Concat::class         => [50, -1],
54
+        BinaryOp\ShiftLeft::class      => [60, -1],
55
+        BinaryOp\ShiftRight::class     => [60, -1],
56
+        BinaryOp\Smaller::class        => [70, 0],
57
+        BinaryOp\SmallerOrEqual::class => [70, 0],
58
+        BinaryOp\Greater::class        => [70, 0],
59
+        BinaryOp\GreaterOrEqual::class => [70, 0],
60
+        BinaryOp\Equal::class          => [80, 0],
61
+        BinaryOp\NotEqual::class       => [80, 0],
62
+        BinaryOp\Identical::class      => [80, 0],
63
+        BinaryOp\NotIdentical::class   => [80, 0],
64
+        BinaryOp\Spaceship::class      => [80, 0],
65
+        BinaryOp\BitwiseAnd::class     => [90, -1],
66 66
         BinaryOp\BitwiseXor::class     => [100, -1],
67 67
         BinaryOp\BitwiseOr::class      => [110, -1],
68 68
         BinaryOp\BooleanAnd::class     => [120, -1],
69 69
         BinaryOp\BooleanOr::class      => [130, -1],
70
-        BinaryOp\Coalesce::class       => [140,  1],
71
-        Expr\Ternary::class            => [150,  0],
70
+        BinaryOp\Coalesce::class       => [140, 1],
71
+        Expr\Ternary::class            => [150, 0],
72 72
         // parser uses %left for assignments, but they really behave as %right
73
-        Expr\Assign::class             => [160,  1],
74
-        Expr\AssignRef::class          => [160,  1],
75
-        AssignOp\Plus::class           => [160,  1],
76
-        AssignOp\Minus::class          => [160,  1],
77
-        AssignOp\Mul::class            => [160,  1],
78
-        AssignOp\Div::class            => [160,  1],
79
-        AssignOp\Concat::class         => [160,  1],
80
-        AssignOp\Mod::class            => [160,  1],
81
-        AssignOp\BitwiseAnd::class     => [160,  1],
82
-        AssignOp\BitwiseOr::class      => [160,  1],
83
-        AssignOp\BitwiseXor::class     => [160,  1],
84
-        AssignOp\ShiftLeft::class      => [160,  1],
85
-        AssignOp\ShiftRight::class     => [160,  1],
86
-        AssignOp\Pow::class            => [160,  1],
87
-        AssignOp\Coalesce::class       => [160,  1],
88
-        Expr\YieldFrom::class          => [165,  1],
89
-        Expr\Print_::class             => [168,  1],
73
+        Expr\Assign::class             => [160, 1],
74
+        Expr\AssignRef::class          => [160, 1],
75
+        AssignOp\Plus::class           => [160, 1],
76
+        AssignOp\Minus::class          => [160, 1],
77
+        AssignOp\Mul::class            => [160, 1],
78
+        AssignOp\Div::class            => [160, 1],
79
+        AssignOp\Concat::class         => [160, 1],
80
+        AssignOp\Mod::class            => [160, 1],
81
+        AssignOp\BitwiseAnd::class     => [160, 1],
82
+        AssignOp\BitwiseOr::class      => [160, 1],
83
+        AssignOp\BitwiseXor::class     => [160, 1],
84
+        AssignOp\ShiftLeft::class      => [160, 1],
85
+        AssignOp\ShiftRight::class     => [160, 1],
86
+        AssignOp\Pow::class            => [160, 1],
87
+        AssignOp\Coalesce::class       => [160, 1],
88
+        Expr\YieldFrom::class          => [165, 1],
89
+        Expr\Print_::class             => [168, 1],
90 90
         BinaryOp\LogicalAnd::class     => [170, -1],
91 91
         BinaryOp\LogicalXor::class     => [180, -1],
92 92
         BinaryOp\LogicalOr::class      => [190, -1],
@@ -147,7 +147,7 @@  discard block
 block discarded – undo
147 147
      * @param array $options Dictionary of formatting options
148 148
      */
149 149
     public function __construct(array $options = []) {
150
-        $this->docStringEndToken = '_DOC_STRING_END_' . mt_rand();
150
+        $this->docStringEndToken = '_DOC_STRING_END_'.mt_rand();
151 151
 
152 152
         $defaultOptions = ['shortArraySyntax' => false];
153 153
         $this->options = $options + $defaultOptions;
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
      */
170 170
     protected function setIndentLevel(int $level) {
171 171
         $this->indentLevel = $level;
172
-        $this->nl = "\n" . \str_repeat(' ', $level);
172
+        $this->nl = "\n".\str_repeat(' ', $level);
173 173
     }
174 174
 
175 175
     /**
@@ -186,7 +186,7 @@  discard block
 block discarded – undo
186 186
     protected function outdent() {
187 187
         assert($this->indentLevel >= 4);
188 188
         $this->indentLevel -= 4;
189
-        $this->nl = "\n" . str_repeat(' ', $this->indentLevel);
189
+        $this->nl = "\n".str_repeat(' ', $this->indentLevel);
190 190
     }
191 191
 
192 192
     /**
@@ -227,7 +227,7 @@  discard block
 block discarded – undo
227 227
             return "<?php\n\n";
228 228
         }
229 229
 
230
-        $p = "<?php\n\n" . $this->prettyPrint($stmts);
230
+        $p = "<?php\n\n".$this->prettyPrint($stmts);
231 231
 
232 232
         if ($stmts[0] instanceof Stmt\InlineHTML) {
233 233
             $p = preg_replace('/^<\?php\s+\?>\n?/', '', $p);
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
      */
264 264
     protected function handleMagicTokens(string $str) : string {
265 265
         // Replace doc-string-end tokens with nothing or a newline
266
-        $str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
266
+        $str = str_replace($this->docStringEndToken.";\n", ";\n", $str);
267 267
         $str = str_replace($this->docStringEndToken, "\n", $str);
268 268
 
269 269
         return $str;
@@ -286,13 +286,13 @@  discard block
 block discarded – undo
286 286
         foreach ($nodes as $node) {
287 287
             $comments = $node->getComments();
288 288
             if ($comments) {
289
-                $result .= $this->nl . $this->pComments($comments);
289
+                $result .= $this->nl.$this->pComments($comments);
290 290
                 if ($node instanceof Stmt\Nop) {
291 291
                     continue;
292 292
                 }
293 293
             }
294 294
 
295
-            $result .= $this->nl . $this->p($node);
295
+            $result .= $this->nl.$this->p($node);
296 296
         }
297 297
 
298 298
         if ($indent) {
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
      */
332 332
     protected function pPrefixOp(string $class, string $operatorString, Node $node) : string {
333 333
         list($precedence, $associativity) = $this->precedenceMap[$class];
334
-        return $operatorString . $this->pPrec($node, $precedence, $associativity, 1);
334
+        return $operatorString.$this->pPrec($node, $precedence, $associativity, 1);
335 335
     }
336 336
 
337 337
     /**
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
      */
346 346
     protected function pPostfixOp(string $class, Node $node, string $operatorString) : string {
347 347
         list($precedence, $associativity) = $this->precedenceMap[$class];
348
-        return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString;
348
+        return $this->pPrec($node, $precedence, $associativity, -1).$operatorString;
349 349
     }
350 350
 
351 351
     /**
@@ -367,7 +367,7 @@  discard block
 block discarded – undo
367 367
             if ($childPrecedence > $parentPrecedence
368 368
                 || ($parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition)
369 369
             ) {
370
-                return '(' . $this->p($node) . ')';
370
+                return '('.$this->p($node).')';
371 371
             }
372 372
         }
373 373
 
@@ -425,10 +425,10 @@  discard block
 block discarded – undo
425 425
             if ($node !== null) {
426 426
                 $comments = $node->getComments();
427 427
                 if ($comments) {
428
-                    $result .= $this->nl . $this->pComments($comments);
428
+                    $result .= $this->nl.$this->pComments($comments);
429 429
                 }
430 430
 
431
-                $result .= $this->nl . $this->p($node);
431
+                $result .= $this->nl.$this->p($node);
432 432
             } else {
433 433
                 $result .= $this->nl;
434 434
             }
@@ -497,14 +497,14 @@  discard block
 block discarded – undo
497 497
         } else {
498 498
             // Fallback
499 499
             // TODO Add <?php properly
500
-            $result = "<?php\n" . $this->pStmts($stmts, false);
500
+            $result = "<?php\n".$this->pStmts($stmts, false);
501 501
         }
502 502
 
503 503
         return ltrim($this->handleMagicTokens($result));
504 504
     }
505 505
 
506 506
     protected function pFallback(Node $node) {
507
-        return $this->{'p' . $node->getType()}($node);
507
+        return $this->{'p'.$node->getType()}($node);
508 508
     }
509 509
 
510 510
     /**
@@ -520,7 +520,7 @@  discard block
 block discarded – undo
520 520
     protected function p(Node $node, $parentFormatPreserved = false) : string {
521 521
         // No orig tokens means this is a normal pretty print without preservation of formatting
522 522
         if (!$this->origTokens) {
523
-            return $this->{'p' . $node->getType()}($node);
523
+            return $this->{'p'.$node->getType()}($node);
524 524
         }
525 525
 
526 526
         /** @var Node $origNode */
@@ -585,7 +585,7 @@  discard block
 block discarded – undo
585 585
 
586 586
                 if (is_int($subNode) && is_int($origSubNode)) {
587 587
                     // Check if this is a modifier change
588
-                    $key = $type . '->' . $subNodeName;
588
+                    $key = $type.'->'.$subNodeName;
589 589
                     if (!isset($this->modifierChangeMap[$key])) {
590 590
                         return $this->pFallback($fallbackNode);
591 591
                     }
@@ -615,7 +615,7 @@  discard block
 block discarded – undo
615 615
                 }
616 616
 
617 617
                 // A node has been inserted, check if we have insertion information for it
618
-                $key = $type . '->' . $subNodeName;
618
+                $key = $type.'->'.$subNodeName;
619 619
                 if (!isset($this->insertionMap[$key])) {
620 620
                     return $this->pFallback($fallbackNode);
621 621
                 }
@@ -623,7 +623,7 @@  discard block
 block discarded – undo
623 623
                 list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
624 624
                 if (null !== $findToken) {
625 625
                     $subStartPos = $this->origTokens->findRight($pos, $findToken)
626
-                        + (int) !$beforeToken;
626
+                        + (int)!$beforeToken;
627 627
                 } else {
628 628
                     $subStartPos = $pos;
629 629
                 }
@@ -637,7 +637,7 @@  discard block
 block discarded – undo
637 637
 
638 638
             if (null === $subNode) {
639 639
                 // A node has been removed, check if we have removal information for it
640
-                $key = $type . '->' . $subNodeName;
640
+                $key = $type.'->'.$subNodeName;
641 641
                 if (!isset($this->removalMap[$key])) {
642 642
                     return $this->pFallback($fallbackNode);
643 643
                 }
@@ -704,7 +704,7 @@  discard block
 block discarded – undo
704 704
     ) {
705 705
         $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);
706 706
 
707
-        $mapKey = $parentNodeType . '->' . $subNodeName;
707
+        $mapKey = $parentNodeType.'->'.$subNodeName;
708 708
         $insertStr = $this->listInsertionMap[$mapKey] ?? null;
709 709
         $isStmtList = $subNodeName === 'stmts';
710 710
 
@@ -794,14 +794,14 @@  discard block
 block discarded – undo
794 794
                         if ($insertNewline) {
795 795
                             $delayedAddComments = $delayedAddNode->getComments();
796 796
                             if ($delayedAddComments) {
797
-                                $result .= $this->pComments($delayedAddComments) . $this->nl;
797
+                                $result .= $this->pComments($delayedAddComments).$this->nl;
798 798
                             }
799 799
                         }
800 800
 
801 801
                         $this->safeAppend($result, $this->p($delayedAddNode, true));
802 802
 
803 803
                         if ($insertNewline) {
804
-                            $result .= $insertStr . $this->nl;
804
+                            $result .= $insertStr.$this->nl;
805 805
                         } else {
806 806
                             $result .= $insertStr;
807 807
                         }
@@ -812,7 +812,7 @@  discard block
 block discarded – undo
812 812
 
813 813
                 if ($comments !== $origComments) {
814 814
                     if ($comments) {
815
-                        $result .= $this->pComments($comments) . $this->nl;
815
+                        $result .= $this->pComments($comments).$this->nl;
816 816
                     }
817 817
                 } else {
818 818
                     $result .= $this->origTokens->getTokenCode(
@@ -849,10 +849,10 @@  discard block
 block discarded – undo
849 849
                 $this->setIndentLevel($lastElemIndentLevel);
850 850
 
851 851
                 if ($insertNewline) {
852
-                    $result .= $insertStr . $this->nl;
852
+                    $result .= $insertStr.$this->nl;
853 853
                     $comments = $arrItem->getComments();
854 854
                     if ($comments) {
855
-                        $result .= $this->pComments($comments) . $this->nl;
855
+                        $result .= $this->pComments($comments).$this->nl;
856 856
                     }
857 857
                 } else {
858 858
                     $result .= $insertStr;
@@ -969,27 +969,27 @@  discard block
 block discarded – undo
969 969
                 if ($this->callLhsRequiresParens($subNode)
970 970
                     && !$this->origTokens->haveParens($subStartPos, $subEndPos)
971 971
                 ) {
972
-                    return '(' . $this->p($subNode) . ')';
972
+                    return '('.$this->p($subNode).')';
973 973
                 }
974 974
                 break;
975 975
             case self::FIXUP_DEREF_LHS:
976 976
                 if ($this->dereferenceLhsRequiresParens($subNode)
977 977
                     && !$this->origTokens->haveParens($subStartPos, $subEndPos)
978 978
                 ) {
979
-                    return '(' . $this->p($subNode) . ')';
979
+                    return '('.$this->p($subNode).')';
980 980
                 }
981 981
                 break;
982 982
             case self::FIXUP_STATIC_DEREF_LHS:
983 983
                 if ($this->staticDereferenceLhsRequiresParens($subNode)
984 984
                     && !$this->origTokens->haveParens($subStartPos, $subEndPos)
985 985
                 ) {
986
-                    return '(' . $this->p($subNode) . ')';
986
+                    return '('.$this->p($subNode).')';
987 987
                 }
988 988
                 break;
989 989
             case self::FIXUP_NEW:
990 990
                 if ($this->newOperandRequiresParens($subNode)
991 991
                     && !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
992
-                    return '(' . $this->p($subNode) . ')';
992
+                    return '('.$this->p($subNode).')';
993 993
                 }
994 994
                 break;
995 995
             case self::FIXUP_BRACED_NAME:
@@ -998,14 +998,14 @@  discard block
 block discarded – undo
998 998
                     && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
999 999
                 ) {
1000 1000
                     return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
1001
-                        . '{' . $this->p($subNode) . '}';
1001
+                        . '{'.$this->p($subNode).'}';
1002 1002
                 }
1003 1003
                 break;
1004 1004
             case self::FIXUP_ENCAPSED:
1005 1005
                 if (!$subNode instanceof Scalar\EncapsedStringPart
1006 1006
                     && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
1007 1007
                 ) {
1008
-                    return '{' . $this->p($subNode) . '}';
1008
+                    return '{'.$this->p($subNode).'}';
1009 1009
                 }
1010 1010
                 break;
1011 1011
             default:
@@ -1039,7 +1039,7 @@  discard block
 block discarded – undo
1039 1039
                 || !$this->labelCharMap[$str[\strlen($str) - 1]]) {
1040 1040
             $str .= $append;
1041 1041
         } else {
1042
-            $str .= " " . $append;
1042
+            $str .= " ".$append;
1043 1043
         }
1044 1044
     }
1045 1045
 
@@ -1127,13 +1127,13 @@  discard block
 block discarded – undo
1127 1127
      * @return string Printed modifiers
1128 1128
      */
1129 1129
     protected function pModifiers(int $modifiers) {
1130
-        return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC    ? 'public '    : '')
1130
+        return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '')
1131 1131
              . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
1132
-             . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE   ? 'private '   : '')
1133
-             . ($modifiers & Stmt\Class_::MODIFIER_STATIC    ? 'static '    : '')
1134
-             . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT  ? 'abstract '  : '')
1135
-             . ($modifiers & Stmt\Class_::MODIFIER_FINAL     ? 'final '     : '')
1136
-             . ($modifiers & Stmt\Class_::MODIFIER_READONLY  ? 'readonly '  : '');
1132
+             . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
1133
+             . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
1134
+             . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
1135
+             . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '')
1136
+             . ($modifiers & Stmt\Class_::MODIFIER_READONLY ? 'readonly ' : '');
1137 1137
     }
1138 1138
 
1139 1139
     /**
@@ -1195,7 +1195,7 @@  discard block
 block discarded – undo
1195 1195
     protected function initializeNodeListDiffer() {
1196 1196
         if ($this->nodeListDiffer) return;
1197 1197
 
1198
-        $this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
1198
+        $this->nodeListDiffer = new Internal\Differ(function($a, $b) {
1199 1199
             if ($a instanceof Node && $b instanceof Node) {
1200 1200
                 return $a === $b->getAttribute('origNode');
1201 1201
             }
Please login to merge, or discard this patch.
Braces   +25 added lines, -10 removed lines patch added patch discarded remove patch
@@ -12,8 +12,7 @@  discard block
 block discarded – undo
12 12
 use PhpParser\Node\Scalar;
13 13
 use PhpParser\Node\Stmt;
14 14
 
15
-abstract class PrettyPrinterAbstract
16
-{
15
+abstract class PrettyPrinterAbstract {
17 16
     const FIXUP_PREC_LEFT       = 0; // LHS operand affected by precedence
18 17
     const FIXUP_PREC_RIGHT      = 1; // RHS operand affected by precedence
19 18
     const FIXUP_CALL_LHS        = 2; // LHS of call
@@ -1176,7 +1175,9 @@  discard block
 block discarded – undo
1176 1175
      * The label char map determines whether a certain character may occur in a label.
1177 1176
      */
1178 1177
     protected function initializeLabelCharMap() {
1179
-        if ($this->labelCharMap) return;
1178
+        if ($this->labelCharMap) {
1179
+        	return;
1180
+        }
1180 1181
 
1181 1182
         $this->labelCharMap = [];
1182 1183
         for ($i = 0; $i < 256; $i++) {
@@ -1193,7 +1194,9 @@  discard block
 block discarded – undo
1193 1194
      * The node list differ is used to determine differences between two array subnodes.
1194 1195
      */
1195 1196
     protected function initializeNodeListDiffer() {
1196
-        if ($this->nodeListDiffer) return;
1197
+        if ($this->nodeListDiffer) {
1198
+        	return;
1199
+        }
1197 1200
 
1198 1201
         $this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
1199 1202
             if ($a instanceof Node && $b instanceof Node) {
@@ -1211,7 +1214,9 @@  discard block
 block discarded – undo
1211 1214
      * some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
1212 1215
      */
1213 1216
     protected function initializeFixupMap() {
1214
-        if ($this->fixupMap) return;
1217
+        if ($this->fixupMap) {
1218
+        	return;
1219
+        }
1215 1220
 
1216 1221
         $this->fixupMap = [
1217 1222
             Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT],
@@ -1309,7 +1314,9 @@  discard block
 block discarded – undo
1309 1314
      * certain node is replaced by null.
1310 1315
      */
1311 1316
     protected function initializeRemovalMap() {
1312
-        if ($this->removalMap) return;
1317
+        if ($this->removalMap) {
1318
+        	return;
1319
+        }
1313 1320
 
1314 1321
         $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
1315 1322
         $stripLeft = ['left' => \T_WHITESPACE];
@@ -1355,7 +1362,9 @@  discard block
 block discarded – undo
1355 1362
     }
1356 1363
 
1357 1364
     protected function initializeInsertionMap() {
1358
-        if ($this->insertionMap) return;
1365
+        if ($this->insertionMap) {
1366
+        	return;
1367
+        }
1359 1368
 
1360 1369
         // TODO: "yield" where both key and value are inserted doesn't work
1361 1370
         // [$find, $beforeToken, $extraLeft, $extraRight]
@@ -1398,7 +1407,9 @@  discard block
 block discarded – undo
1398 1407
     }
1399 1408
 
1400 1409
     protected function initializeListInsertionMap() {
1401
-        if ($this->listInsertionMap) return;
1410
+        if ($this->listInsertionMap) {
1411
+        	return;
1412
+        }
1402 1413
 
1403 1414
         $this->listInsertionMap = [
1404 1415
             // special
@@ -1492,7 +1503,9 @@  discard block
 block discarded – undo
1492 1503
     }
1493 1504
 
1494 1505
     protected function initializeEmptyListInsertionMap() {
1495
-        if ($this->emptyListInsertionMap) return;
1506
+        if ($this->emptyListInsertionMap) {
1507
+        	return;
1508
+        }
1496 1509
 
1497 1510
         // TODO Insertion into empty statement lists.
1498 1511
 
@@ -1555,7 +1568,9 @@  discard block
 block discarded – undo
1555 1568
     }
1556 1569
 
1557 1570
     protected function initializeModifierChangeMap() {
1558
-        if ($this->modifierChangeMap) return;
1571
+        if ($this->modifierChangeMap) {
1572
+        	return;
1573
+        }
1559 1574
 
1560 1575
         $this->modifierChangeMap = [
1561 1576
             'Stmt_ClassConst->flags' => \T_CONST,
Please login to merge, or discard this patch.