@prettier/plugin-php 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -176,17 +176,33 @@ await prettier.format(YOUR_CODE, {
176
176
 
177
177
  Prettier for PHP supports the following options. We recommend that all users set the `phpVersion` option.
178
178
 
179
- | Name | Default | Description |
180
- | ------------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
- | `phpVersion` | `"7.0"` | Allows specifying the PHP version you're using. If you're using PHP 7.1 or later, setting this option will make use of modern language features in the printed output. If you're using PHP lower than 7.0, you'll have to set this option or Prettier will generate incompatible code. |
182
- | `printWidth` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)) |
183
- | `tabWidth` | `4` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)), The default is `4` based on the `PSR-2` coding standard. |
184
- | `useTabs` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tabs)) |
185
- | `singleQuote` | `false` | If set to `"true"`, strings that use double quotes but do not rely on the features they add, will be reformatted. Example: `"foo" -> 'foo'`, `"foo $bar" -> "foo $bar"`. |
186
- | `trailingCommaPHP` | `true` | If set to `true`, trailing commas will be added wherever possible. <br> If set to `false`, no trailing commas are printed. |
187
- | `braceStyle` | `"per-cs"` | If set to `"per-cs"`, prettier will move open brace for code blocks (classes, functions and methods) onto new line. <br> If set to `"1tbs"`, prettier will move open brace for code blocks (classes, functions and methods) onto same line. |
188
- | `requirePragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)) |
189
- | `insertPragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#insert-pragma)) |
179
+ | Name | Default | Description |
180
+ | ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | `phpVersion` | `"auto"` \* | Allows specifying the PHP version you're using. (See Notes Below) |
182
+ | `printWidth` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)) |
183
+ | `tabWidth` | `4` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)), The default is `4` based on the `PSR-2` coding standard. |
184
+ | `useTabs` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tabs)) |
185
+ | `singleQuote` | `false` | If set to `"true"`, strings that use double quotes but do not rely on the features they add, will be reformatted. Example: `"foo" -> 'foo'`, `"foo $bar" -> "foo $bar"`. |
186
+ | `trailingCommaPHP` | `true` | If set to `true`, trailing commas will be added wherever possible. <br> If set to `false`, no trailing commas are printed. |
187
+ | `braceStyle` | `"per-cs"` | If set to `"per-cs"`, prettier will move open brace for code blocks (classes, functions and methods) onto new line. <br> If set to `"1tbs"`, prettier will move open brace for code blocks (classes, functions and methods) onto same line. |
188
+ | `requirePragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)) |
189
+ | `insertPragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#insert-pragma)) |
190
+
191
+ ### \* `phpVersion` Configuration Notes :
192
+
193
+ The default setting `auto`, attempts to auto-detect your supported php versions from a `composer.json` with in the
194
+ current directory or any parent directory, the plugin will use a minimum supported php version from
195
+ `{"require":{"php":"..."}}` to set the phpVersion. If not found or not able to be parsed, it will default to latest
196
+ supported PHP version.
197
+
198
+ You set the `phpVersion` to `composer` and this will only use the `composer.json` file to determine the php
199
+ version, prettier will crash if the PHP cannot be determined.
200
+
201
+ You can also set the `phpVersion` to a specific version, such as `7.4`, `8.0`, `8.1`, `8.2`, `8.3` or `8.4`.
202
+
203
+ **Please Note:** If the phpVersion is not set correctly for your environment, this plugin will produce code that could
204
+ be incompatible with your PHP runtime. For example, if you are using PHP 7.4, but the plugin is set to PHP 8.3, it will
205
+ produce code that uses features not available in PHP 7.4.
190
206
 
191
207
  ## Ignoring code
192
208
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prettier/plugin-php",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Prettier PHP Plugin",
5
5
  "repository": "prettier/prettier-php",
6
6
  "author": "Lucas Azzola <@azz>",
@@ -27,32 +27,32 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "linguist-languages": "^8.0.0",
30
- "php-parser": "^3.2.5"
30
+ "php-parser": "^3.4.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@babel/preset-env": "^7.27.2",
34
34
  "@rollup/plugin-alias": "^5.1.0",
35
- "@rollup/plugin-babel": "^6.0.3",
36
- "@rollup/plugin-commonjs": "^25.0.7",
35
+ "@rollup/plugin-babel": "^7.0.0",
36
+ "@rollup/plugin-commonjs": "^29.0.2",
37
37
  "@rollup/plugin-inject": "^5.0.5",
38
38
  "@rollup/plugin-json": "^6.1.0",
39
- "@rollup/plugin-node-resolve": "^15.2.1",
40
- "@rollup/plugin-replace": "^5.0.5",
39
+ "@rollup/plugin-node-resolve": "^16.0.3",
40
+ "@rollup/plugin-replace": "^6.0.3",
41
41
  "@rollup/plugin-terser": "^0.4.3",
42
42
  "c8": "^10.1.3",
43
43
  "cross-env": "^7.0.2",
44
44
  "eslint": "8.57.0",
45
45
  "eslint-config-prettier": "9.1.0",
46
- "eslint-plugin-import": "2.29.1",
46
+ "eslint-plugin-import": "2.32.0",
47
47
  "eslint-plugin-jest": "27.6.3",
48
- "eslint-plugin-prettier": "5.1.3",
48
+ "eslint-plugin-prettier": "5.5.5",
49
49
  "eslint-plugin-prettier-doc": "^1.1.0",
50
- "jest": "30.0.3",
51
- "jest-environment-jsdom": "30.0.2",
50
+ "jest": "30.3.0",
51
+ "jest-environment-jsdom": "30.3.0",
52
52
  "jest-light-runner": "^0.7.9",
53
53
  "jest-snapshot-serializer-raw": "^2.0.0",
54
54
  "prettier": "^3.6.1",
55
- "rollup": "^3.28.1",
55
+ "rollup": "^4.60.0",
56
56
  "strip-ansi": "^7.1.0"
57
57
  },
58
58
  "peerDependencies": {
@@ -1,9 +1,4 @@
1
- import {
2
- getPrecedence,
3
- shouldFlatten,
4
- isBitwiseOperator,
5
- isMinVersion,
6
- } from "./util.mjs";
1
+ import { getPrecedence, shouldFlatten, isBitwiseOperator } from "./util.mjs";
7
2
 
8
3
  function needsParens(path, options) {
9
4
  const { parent } = path;
@@ -135,7 +130,7 @@ function needsParens(path, options) {
135
130
  case "new": {
136
131
  const requiresParens =
137
132
  node.kind === "clone" ||
138
- (node.kind === "new" && !isMinVersion(options.phpVersion, "8.4"));
133
+ (node.kind === "new" && options.phpVersion < 8.4);
139
134
  switch (parent.kind) {
140
135
  case "propertylookup":
141
136
  case "nullsafepropertylookup":
@@ -147,7 +142,8 @@ function needsParens(path, options) {
147
142
  return false;
148
143
  }
149
144
  }
150
- case "yield": {
145
+ case "yield":
146
+ case "yieldfrom": {
151
147
  switch (parent.kind) {
152
148
  case "propertylookup":
153
149
  case "nullsafepropertylookup":
@@ -159,8 +155,11 @@ function needsParens(path, options) {
159
155
  case "retif":
160
156
  return key === "test";
161
157
 
158
+ case "assign":
159
+ return node.kind === "yield" && !!(node.key || node.value);
160
+
162
161
  default:
163
- return !!(node.key || node.value);
162
+ return node.kind === "yieldfrom" || !!(node.key || node.value);
164
163
  }
165
164
  }
166
165
  case "assign": {
@@ -171,7 +170,7 @@ function needsParens(path, options) {
171
170
  return false;
172
171
  } else if (parent.kind === "assign") {
173
172
  return false;
174
- } else if (parent.kind === "static") {
173
+ } else if (parent.kind === "staticvariable") {
175
174
  return false;
176
175
  } else if (
177
176
  ["if", "do", "while", "foreach", "switch"].includes(parent.kind)
@@ -220,7 +219,7 @@ function needsParens(path, options) {
220
219
  default:
221
220
  return false;
222
221
  }
223
- case "silence":
222
+ case "silent":
224
223
  case "cast":
225
224
  // TODO: bug https://github.com/glayzzle/php-parser/issues/172
226
225
  return node.parenthesizedExpression;
package/src/options.mjs CHANGED
@@ -1,30 +1,124 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
1
4
  const CATEGORY_PHP = "PHP";
2
5
 
6
+ // prettier-ignore
7
+ const SUPPORTED_PHP_VERSIONS = [
8
+ 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6,
9
+ 7.0, 7.1, 7.2, 7.3, 7.4,
10
+ 8.0, 8.1, 8.2, 8.3, 8.4, 8.5,
11
+ ];
12
+
13
+ export const LATEST_SUPPORTED_PHP_VERSION = Math.max(...SUPPORTED_PHP_VERSIONS);
14
+
15
+ let getComposerError = "";
16
+
17
+ /**
18
+ * Detect the minimum PHP version from the composer.json file
19
+ * @return {number|null} The PHP version to use in the composer.json file, null when not found
20
+ */
21
+ function getComposerPhpVersion() {
22
+ // Try to find composer.json
23
+ const currentDir = process.cwd();
24
+ let composerPath = null;
25
+
26
+ const directComposerPath = path.join(currentDir, "composer.json");
27
+ if (fs.existsSync(directComposerPath)) {
28
+ composerPath = directComposerPath;
29
+ }
30
+
31
+ if (!composerPath) {
32
+ let searchDir = path.dirname(currentDir);
33
+ while (searchDir !== path.parse(searchDir).root) {
34
+ const potentialComposerPath = path.join(searchDir, "composer.json");
35
+ if (fs.existsSync(potentialComposerPath)) {
36
+ composerPath = potentialComposerPath;
37
+ break;
38
+ }
39
+ searchDir = path.dirname(searchDir);
40
+ }
41
+ }
42
+
43
+ if (composerPath) {
44
+ try {
45
+ const fileContent = fs.readFileSync(composerPath, "utf8");
46
+ const composerJson = JSON.parse(fileContent);
47
+
48
+ if (composerJson.require && composerJson.require.php) {
49
+ // Check for a wildcard pattern like "7.*"
50
+ const wildcardMatch = composerJson.require.php.match(
51
+ /^(?:[^0-9]*)?([0-9]+)\.\*/
52
+ );
53
+ if (wildcardMatch) {
54
+ return parseFloat(`${wildcardMatch[1]}.0`);
55
+ }
56
+
57
+ // Extract version from composer semver format
58
+ const versionMatch = composerJson.require.php.match(
59
+ /^(?:[^0-9]*)?([0-9]+)\.([0-9]+)/
60
+ );
61
+
62
+ if (versionMatch) {
63
+ return parseFloat(`${versionMatch[1]}.${versionMatch[2]}`);
64
+ } else {
65
+ getComposerError = `Could not decode PHP version (${composerJson.require.php}})`;
66
+ return null;
67
+ }
68
+ }
69
+ } catch (e) {
70
+ getComposerError = `Error reading composer.json: ${e.message}`;
71
+ }
72
+ } else {
73
+ getComposerError = "Could not find composer.json";
74
+ }
75
+
76
+ return null;
77
+ }
78
+
79
+ export { getComposerPhpVersion };
80
+
81
+ /**
82
+ * Resolve the PHP version to a number based on the provided options.
83
+ *
84
+ */
85
+ export function resolvePhpVersion(options) {
86
+ if (!options) {
87
+ return;
88
+ }
89
+ if (options.phpVersion === "auto") {
90
+ options.phpVersion =
91
+ getComposerPhpVersion() ?? LATEST_SUPPORTED_PHP_VERSION;
92
+ } else if (options.phpVersion === "composer") {
93
+ const v = getComposerPhpVersion();
94
+ if (v === null) {
95
+ throw new Error(
96
+ `Could not determine PHP version from composer; ${getComposerError}`
97
+ );
98
+ }
99
+ options.phpVersion = v;
100
+ } else {
101
+ options.phpVersion = parseFloat(options.phpVersion);
102
+ }
103
+ }
104
+
3
105
  export default {
4
106
  phpVersion: {
5
107
  since: "0.13.0",
6
108
  category: CATEGORY_PHP,
7
109
  type: "choice",
8
- default: "7.0",
110
+ default: "auto",
9
111
  description: "Minimum target PHP version.",
10
112
  choices: [
11
- { value: "5.0" },
12
- { value: "5.1" },
13
- { value: "5.2" },
14
- { value: "5.3" },
15
- { value: "5.4" },
16
- { value: "5.5" },
17
- { value: "5.6" },
18
- { value: "7.0" },
19
- { value: "7.1" },
20
- { value: "7.2" },
21
- { value: "7.3" },
22
- { value: "7.4" },
23
- { value: "8.0" },
24
- { value: "8.1" },
25
- { value: "8.2" },
26
- { value: "8.3" },
27
- { value: "8.4" },
113
+ ...SUPPORTED_PHP_VERSIONS.map((v) => ({ value: v.toFixed(1) })),
114
+ {
115
+ value: "composer",
116
+ description: "Use the PHP version defined in composer.json",
117
+ },
118
+ {
119
+ value: "auto",
120
+ description: `Try composer.json, else latest PHP Version (${LATEST_SUPPORTED_PHP_VERSION})`,
121
+ },
28
122
  ],
29
123
  },
30
124
  trailingCommaPHP: {
package/src/parser.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import engine from "php-parser";
2
- import options from "./options.mjs";
2
+ import { LATEST_SUPPORTED_PHP_VERSION } from "./options.mjs";
3
+ import { resolvePhpVersion } from "./options.mjs";
3
4
 
4
5
  function parse(text, opts) {
5
6
  const inMarkdown = opts && opts.parentParser === "markdown";
@@ -7,19 +8,16 @@ function parse(text, opts) {
7
8
  if (!text && inMarkdown) {
8
9
  return "";
9
10
  }
11
+ resolvePhpVersion(opts);
10
12
 
11
13
  // Todo https://github.com/glayzzle/php-parser/issues/170
12
14
  text = text.replace(/\?>\n<\?/g, "?>\n___PSEUDO_INLINE_PLACEHOLDER___<?");
13
15
 
14
- const latestSupportedPhpVersion = Math.max(
15
- ...options.phpVersion.choices.map((c) => parseFloat(c.value))
16
- );
17
-
18
16
  // initialize a new parser instance
19
17
  const parser = new engine({
20
18
  parser: {
21
19
  extractDoc: true,
22
- version: `${latestSupportedPhpVersion}`,
20
+ version: `${LATEST_SUPPORTED_PHP_VERSION}`,
23
21
  },
24
22
  ast: {
25
23
  withPositions: true,
package/src/printer.mjs CHANGED
@@ -37,7 +37,6 @@ import {
37
37
  isReferenceLikeNode,
38
38
  normalizeMagicMethodName,
39
39
  isSimpleCallArgument,
40
- isMinVersion,
41
40
  } from "./util.mjs";
42
41
 
43
42
  const {
@@ -51,6 +50,7 @@ const {
51
50
  dedent,
52
51
  ifBreak,
53
52
  hardline,
53
+ hardlineWithoutBreakParent,
54
54
  softline,
55
55
  literalline,
56
56
  align,
@@ -66,12 +66,19 @@ const {
66
66
  isPreviousLineEmpty,
67
67
  } = prettierUtil;
68
68
 
69
+ /**
70
+ * Determine if we should print a trailing comma based on the config & php version
71
+ *
72
+ * @param options {object} Prettier Options
73
+ * @param requiredVersion {number}
74
+ * @returns {boolean}
75
+ */
69
76
  function shouldPrintComma(options, requiredVersion) {
70
77
  if (!options.trailingCommaPHP) {
71
78
  return false;
72
79
  }
73
80
 
74
- return isMinVersion(options.phpVersion, requiredVersion);
81
+ return options.phpVersion >= requiredVersion;
75
82
  }
76
83
 
77
84
  function shouldPrintHardlineForOpenBrace(options) {
@@ -612,9 +619,9 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
612
619
  const lastArg = getLast(args);
613
620
 
614
621
  const maybeTrailingComma =
615
- (shouldPrintComma(options, "7.3") &&
622
+ (shouldPrintComma(options, 7.3) &&
616
623
  ["call", "new", "unset", "isset"].includes(node.kind)) ||
617
- (shouldPrintComma(options, "8.0") &&
624
+ (shouldPrintComma(options, 8.0) &&
618
625
  ["function", "closure", "method", "arrowfunc", "attribute"].includes(
619
626
  node.kind
620
627
  ))
@@ -735,6 +742,32 @@ function shouldInlineLogicalExpression(node) {
735
742
  // precedence level and the AST is structured based on precedence
736
743
  // level, things are naturally broken up correctly, i.e. `&&` is
737
744
  // broken before `+`.
745
+ function printPipeChain(path, print, trailingSemicolon = false) {
746
+ const elements = [];
747
+
748
+ function collect() {
749
+ const { node } = path;
750
+ if (node.kind === "bin" && node.type === "|>") {
751
+ elements.push(path.call(print, "left"));
752
+ path.call(collect, "right");
753
+ } else {
754
+ elements.push(print());
755
+ }
756
+ }
757
+
758
+ collect();
759
+
760
+ const [first, ...rest] = elements;
761
+ const semicolon = trailingSemicolon
762
+ ? ifBreak([hardlineWithoutBreakParent, ";"], ";")
763
+ : "";
764
+ return group([
765
+ first,
766
+ indent(rest.flatMap((el) => [line, "|> ", el])),
767
+ semicolon,
768
+ ]);
769
+ }
770
+
738
771
  function printBinaryExpression(
739
772
  path,
740
773
  print,
@@ -1186,7 +1219,7 @@ function printAttrs(path, options, print, { inline = false } = {}) {
1186
1219
  allAttrs.push(
1187
1220
  group([
1188
1221
  indent(attrGroup),
1189
- ifBreak(shouldPrintComma(options, "8.0") ? "," : ""),
1222
+ ifBreak(shouldPrintComma(options, 8.0) ? "," : ""),
1190
1223
  softline,
1191
1224
  "]",
1192
1225
  inline ? ifBreak(softline, " ") : "",
@@ -1213,7 +1246,7 @@ function printClass(path, options, print) {
1213
1246
  declaration.push("abstract ");
1214
1247
  }
1215
1248
 
1216
- if (node.isReadonly) {
1249
+ if (node.isReadonly && !isAnonymousClass) {
1217
1250
  declaration.push("readonly ");
1218
1251
  }
1219
1252
 
@@ -1518,6 +1551,7 @@ function printAssignmentRight(
1518
1551
 
1519
1552
  const canBreak =
1520
1553
  (pureRightNode.kind === "bin" &&
1554
+ pureRightNode.type !== "|>" &&
1521
1555
  !shouldInlineLogicalExpression(pureRightNode)) ||
1522
1556
  (pureRightNode.kind === "retif" &&
1523
1557
  ((!pureRightNode.trueExpr &&
@@ -1658,11 +1692,7 @@ function printNode(path, options, print) {
1658
1692
  join([",", line], path.map(print, "items")),
1659
1693
  ]),
1660
1694
  node.name
1661
- ? [
1662
- ifBreak(shouldPrintComma(options, "7.2") ? "," : ""),
1663
- softline,
1664
- "}",
1665
- ]
1695
+ ? [ifBreak(shouldPrintComma(options, 7.2) ? "," : ""), softline, "}"]
1666
1696
  : "",
1667
1697
  ]);
1668
1698
  case "useitem":
@@ -2092,7 +2122,7 @@ function printNode(path, options, print) {
2092
2122
  () => printAttrs(path, options, print, { inline: true }),
2093
2123
  "what"
2094
2124
  ),
2095
- "class",
2125
+ node.what.isReadonly ? "readonly class" : "class",
2096
2126
  node.arguments.length > 0
2097
2127
  ? [" ", printArgumentsList(path, options, print)]
2098
2128
  : "",
@@ -2356,9 +2386,8 @@ function printNode(path, options, print) {
2356
2386
  case "list":
2357
2387
  case "array": {
2358
2388
  const useShortForm =
2359
- (node.kind === "array" && isMinVersion(options.phpVersion, "5.4")) ||
2360
- (node.kind === "list" &&
2361
- (node.shortForm || isMinVersion(options.phpVersion, "7.1")));
2389
+ (node.kind === "array" && options.phpVersion >= 5.4) ||
2390
+ (node.kind === "list" && (node.shortForm || options.phpVersion >= 7.1));
2362
2391
  const open = useShortForm ? "[" : [node.kind, "("];
2363
2392
  const close = useShortForm ? "]" : ")";
2364
2393
 
@@ -2408,7 +2437,7 @@ function printNode(path, options, print) {
2408
2437
  indent([softline, printArrayItems(path, options, print)]),
2409
2438
  needsForcedTrailingComma ? "," : "",
2410
2439
  ifBreak(
2411
- !needsForcedTrailingComma && shouldPrintComma(options, "5.0")
2440
+ !needsForcedTrailingComma && shouldPrintComma(options, 5.0)
2412
2441
  ? [
2413
2442
  lastElem && shouldPrintHardlineBeforeTrailingComma(lastElem)
2414
2443
  ? hardline
@@ -2486,6 +2515,16 @@ function printNode(path, options, print) {
2486
2515
  );
2487
2516
  }
2488
2517
  case "bin": {
2518
+ if (node.type === "|>") {
2519
+ const { parent, grandparent } = path;
2520
+ const isStatementLevel =
2521
+ parent.kind === "expressionstatement" ||
2522
+ (parent.kind === "assign" &&
2523
+ grandparent &&
2524
+ grandparent.kind === "expressionstatement");
2525
+ return printPipeChain(path, print, isStatementLevel);
2526
+ }
2527
+
2489
2528
  const { parent, grandparent: parentParent } = path;
2490
2529
  const isInsideParenthesis =
2491
2530
  node !== parent.body &&
@@ -2675,7 +2714,7 @@ function printNode(path, options, print) {
2675
2714
  if (parent.kind === "encapsedpart") {
2676
2715
  const parentParent = path.grandparent;
2677
2716
  let closingTagIndentation = 0;
2678
- const flexible = isMinVersion(options.phpVersion, "7.3");
2717
+ const flexible = options.phpVersion >= 7.3;
2679
2718
  let linebreak = literalline;
2680
2719
  if (parentParent.type === "heredoc") {
2681
2720
  linebreak = flexible ? hardline : literalline;
@@ -2744,7 +2783,7 @@ function printNode(path, options, print) {
2744
2783
  case "string":
2745
2784
  case "shell":
2746
2785
  case "heredoc": {
2747
- const flexible = isMinVersion(options.phpVersion, "7.3");
2786
+ const flexible = options.phpVersion >= 7.3;
2748
2787
  const linebreak = flexible ? hardline : literalline;
2749
2788
  return [
2750
2789
  getEncapsedQuotes(node),
@@ -2769,7 +2808,7 @@ function printNode(path, options, print) {
2769
2808
  case "magic":
2770
2809
  return node.value;
2771
2810
  case "nowdoc": {
2772
- const flexible = isMinVersion(options.phpVersion, "7.3");
2811
+ const flexible = options.phpVersion >= 7.3;
2773
2812
  const linebreak = flexible ? hardline : literalline;
2774
2813
  return [
2775
2814
  "<<<'",
package/src/util.mjs CHANGED
@@ -41,6 +41,7 @@ const PRECEDENCE = new Map(
41
41
  "<<=",
42
42
  ">>=",
43
43
  ],
44
+ ["|>"],
44
45
  ["??"],
45
46
  ["||"],
46
47
  ["&&"],
@@ -70,7 +71,7 @@ const bitshiftOperators = [">>", "<<"];
70
71
 
71
72
  function isBitwiseOperator(operator) {
72
73
  return (
73
- !!bitshiftOperators[operator] ||
74
+ bitshiftOperators.includes(operator) ||
74
75
  operator === "|" ||
75
76
  operator === "^" ||
76
77
  operator === "&"
@@ -359,6 +360,17 @@ function lineShouldEndWithSemicolon(path) {
359
360
  return true;
360
361
  }
361
362
  }
363
+ if (node.kind === "expressionstatement") {
364
+ const expr = node.expression;
365
+ const isPipeChain =
366
+ (expr.kind === "bin" && expr.type === "|>") ||
367
+ (expr.kind === "assign" &&
368
+ expr.right.kind === "bin" &&
369
+ expr.right.type === "|>");
370
+ if (isPipeChain) {
371
+ return false;
372
+ }
373
+ }
362
374
  return [
363
375
  "expressionstatement",
364
376
  "do",
@@ -678,7 +690,7 @@ function isSimpleCallArgument(node, depth = 2) {
678
690
 
679
691
  if (isCallLikeExpression(node)) {
680
692
  const parts = getChainParts(node);
681
- parts.unshift();
693
+ parts.shift();
682
694
 
683
695
  return (
684
696
  parts.length <= depth &&
@@ -709,10 +721,6 @@ function memoize(fn) {
709
721
  };
710
722
  }
711
723
 
712
- function isMinVersion(actualVersion, requiredVersion) {
713
- return parseFloat(actualVersion) >= parseFloat(requiredVersion);
714
- }
715
-
716
724
  export {
717
725
  printNumber,
718
726
  getPrecedence,
@@ -744,5 +752,4 @@ export {
744
752
  normalizeMagicMethodName,
745
753
  isSimpleCallArgument,
746
754
  memoize,
747
- isMinVersion,
748
755
  };