Repository: prettier/plugin-php
Pull request #2228 (2023-08-28 03:14:34)

Fix magic methods detection
Input:

```php
<?php
class Foo
{
    public function __pRoTo__() {}
    public function cOnStRuCtOr() {}
}
```

Output:

```php
<?php
class Foo
{
    public function Have not implemented kind undefined yet.()
    {
    }
    public function Have not implemented kind undefined yet.()
    {
    }
}
```


Patch:
diff --git a/src/comments.js b/src/comments.js
index 84db282..3406741 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -5,6 +5,7 @@ import {
   isPreviousLineEmpty,
   isLookupNode,
 } from "./util.js";
+import { locStart, locEnd } from "./loc.js";
 
 const {
   addLeadingComment,
@@ -380,16 +381,12 @@ function handleRetifComments(
   precedingNode,
   followingNode,
   comment,
-  text,
-  options
+  text
+  /* options */
 ) {
   const isSameLineAsPrecedingNode =
     precedingNode &&
-    !hasNewlineInRange(
-      text,
-      options.locEnd(precedingNode),
-      options.locStart(comment)
-    );
+    !hasNewlineInRange(text, locEnd(precedingNode), locStart(comment));
 
   if (
     (!precedingNode || !isSameLineAsPrecedingNode) &&
@@ -518,14 +515,14 @@ function handleFunction(text, enclosingNode, followingNode, comment, options) {
     let argumentsLocEnd = 0;
     for (let i = 0; i < enclosingNode.arguments.length; i++) {
       argumentsLocEnd =
-        options.locEnd(enclosingNode.arguments[i]) > argumentsLocEnd
-          ? options.locEnd(enclosingNode.arguments[i])
+        locEnd(enclosingNode.arguments[i]) > argumentsLocEnd
+          ? locEnd(enclosingNode.arguments[i])
           : argumentsLocEnd;
     }
     const commentIsBetweenArgumentsAndBody =
       enclosingNode.body &&
-      options.locStart(comment) > argumentsLocEnd &&
-      options.locEnd(comment) < options.locStart(enclosingNode.body);
+      locStart(comment) > argumentsLocEnd &&
+      locEnd(comment) < locStart(enclosingNode.body);
     const nextCharIndex = getNextNonSpaceNonCommentCharacterIndex(
       text,
       comment,
@@ -538,7 +535,7 @@ function handleFunction(text, enclosingNode, followingNode, comment, options) {
       commentIsBetweenArgumentsAndBody &&
       text.charAt(nextCharIndex) !== ")"
     ) {
-      if (options.locEnd(comment) < options.locStart(enclosingNode.type)) {
+      if (locEnd(comment) < locStart(enclosingNode.type)) {
         // we need to store this as a dangling comment in case the type is nullable
         // ie function(): ?string {} - the "nullable" attribute is part of the
         // function node, not the type.
@@ -868,11 +865,11 @@ function hasTrailingComment(node) {
   return node.comments && node.comments.some((comment) => comment.trailing);
 }
 
-function hasLeadingOwnLineComment(text, node, options) {
+function hasLeadingOwnLineComment(text, node) {
   return (
     node.comments &&
     node.comments.some(
-      (comment) => comment.leading && hasNewline(text, options.locEnd(comment))
+      (comment) => comment.leading && hasNewline(text, locEnd(comment))
     )
   );
 }
@@ -941,9 +938,7 @@ function printLeadingComment(path, print, options) {
   if (isBlock) {
     return [
       contents,
-      hasNewline(options.originalText, options.locEnd(comment))
-        ? hardline
-        : " ",
+      hasNewline(options.originalText, locEnd(comment)) ? hardline : " ",
     ];
   }
 
@@ -961,7 +956,7 @@ function printTrailingComment(path, print, options) {
     options.printer.isBlockComment && options.printer.isBlockComment(comment);
 
   if (
-    hasNewline(options.originalText, options.locStart(comment), {
+    hasNewline(options.originalText, locStart(comment), {
       backwards: true,
     })
   ) {
@@ -1015,7 +1010,7 @@ function printAllComments(path, print, options, needsSemi) {
       leadingParts.push(contents);
 
       const text = options.originalText;
-      if (hasNewline(text, skipNewline(text, options.locEnd(comment)))) {
+      if (hasNewline(text, skipNewline(text, locEnd(comment)))) {
         leadingParts.push(hardline);
       }
     } else if (trailing) {
diff --git a/src/index.js b/src/index.js
index 8a862e2..c6b8cf2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -16,6 +16,7 @@ import {
   isBlockComment,
 } from "./comments.js";
 import { hasPragma, insertPragma } from "./pragma.js";
+import { locStart, locEnd } from "./loc.js";
 
 const { join, hardline } = doc.builders;
 
@@ -55,16 +56,12 @@ const languages = [
   }),
 ];
 
-const loc = (prop) => (node) => {
-  return node.loc && node.loc[prop] && node.loc[prop].offset;
-};
-
 const parsers = {
   php: {
     parse,
     astFormat: "php",
-    locStart: loc("start"),
-    locEnd: loc("end"),
+    locStart,
+    locEnd,
     hasPragma,
   },
 };
diff --git a/src/loc.js b/src/loc.js
new file mode 100644
index 0000000..b5175e8
--- /dev/null
+++ b/src/loc.js
@@ -0,0 +1,6 @@
+const loc = (prop) => (node) => {
+  return node.loc && node.loc[prop] && node.loc[prop].offset;
+};
+
+export const locStart = loc("start");
+export const locEnd = loc("end");
diff --git a/src/printer.js b/src/printer.js
index 1b00955..44ce694 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -28,6 +28,7 @@ import {
   hasLeadingOwnLineComment,
 } from "./comments.js";
 import pathNeedsParens from "./needs-parens.js";
+import { locStart, locEnd } from "./loc.js";
 
 import {
   getLast,
@@ -1069,8 +1070,8 @@ function printLines(path, options, print, childrenAttribute = "children") {
         ? [
             hasNewlineInRange(
               options.originalText.trimEnd(),
-              options.locEnd(lastNode),
-              options.locEnd(node)
+              locEnd(lastNode),
+              locEnd(node)
             )
               ? !(
                   lastNode.kind === "inline" &&
@@ -1502,7 +1503,7 @@ function printAssignmentRight(
 ) {
   const ref = hasRef ? "&" : "";
 
-  if (hasLeadingOwnLineComment(options.originalText, rightNode, options)) {
+  if (hasLeadingOwnLineComment(options.originalText, rightNode)) {
     return indent([hardline, ref, printedRight]);
   }
 
@@ -1545,13 +1546,13 @@ function stringHasNewLines(node) {
   return node.raw.includes("\n");
 }
 
-function isStringOnItsOwnLine(node, text, options) {
+function isStringOnItsOwnLine(node, text) {
   return (
     (node.kind === "string" ||
       (node.kind === "encapsed" &&
         (node.type === "string" || node.type === "shell"))) &&
     stringHasNewLines(node) &&
-    !hasNewline(text, options.locStart(node), { backwards: true })
+    !hasNewline(text, locStart(node), { backwards: true })
   );
 }
 
@@ -1632,11 +1633,7 @@ function printNode(path, options, print) {
                   ? ""
                   : [
                       hardline,
-                      isNextLineEmptyAfterNamespace(
-                        options.originalText,
-                        node,
-                        options.locStart
-                      )
+                      isNextLineEmptyAfterNamespace(options.originalText, node)
                         ? hardline
                         : "",
                     ],
@@ -2039,7 +2036,7 @@ function printNode(path, options, print) {
       // Multiline strings as single arguments
       if (
         node.arguments.length === 1 &&
-        isStringOnItsOwnLine(node.arguments[0], options.originalText, options)
+        isStringOnItsOwnLine(node.arguments[0], options.originalText)
       ) {
         return [
           print("what"),
@@ -2064,7 +2061,7 @@ function printNode(path, options, print) {
       if (
         !isAnonymousClassNode &&
         node.arguments.length === 1 &&
-        isStringOnItsOwnLine(node.arguments[0], options.originalText, options)
+        isStringOnItsOwnLine(node.arguments[0], options.originalText)
       ) {
         return [
           "new ",
@@ -2156,7 +2153,7 @@ function printNode(path, options, print) {
         node.useDie ? "die" : "exit",
         "(",
         node.expression
-          ? isStringOnItsOwnLine(node.expression, options.originalText, options)
+          ? isStringOnItsOwnLine(node.expression, options.originalText)
             ? print("expression")
             : [indent([softline, print("expression")]), softline]
           : printDanglingComments(path, options),
@@ -2199,7 +2196,7 @@ function printNode(path, options, print) {
     case "eval":
       return group([
         "eval(",
-        isStringOnItsOwnLine(node.source, options.originalText, options)
+        isStringOnItsOwnLine(node.source, options.originalText)
           ? print("source")
           : [indent([softline, print("source")]), softline],
         ")",
@@ -2388,15 +2385,15 @@ function printNode(path, options, print) {
 
       const [firstProperty] = node.items
         .filter((node) => node.kind !== "noop")
-        .sort((a, b) => options.locStart(a) - options.locStart(b));
+        .sort((a, b) => locStart(a) - locStart(b));
       const isAssociative = !!(firstProperty && firstProperty.key);
       const shouldBreak =
         isAssociative &&
         firstProperty &&
         hasNewlineInRange(
           options.originalText,
-          options.locStart(node),
-          options.locStart(firstProperty)
+          locStart(node),
+          locStart(firstProperty)
         );
 
       return group(
@@ -2888,7 +2885,7 @@ function printNode(path, options, print) {
     case "error":
     default:
       // istanbul ignore next
-      return `Have not implemented kind ${node.kind} yet.`;
+      throw new Error(`Have not implemented kind '${node.kind}' yet.`);
   }
 }
 
diff --git a/src/util.js b/src/util.js
index e8c6b59..9466f91 100644
--- a/src/util.js
+++ b/src/util.js
@@ -1,4 +1,5 @@
 import { util as prettierUtil, version as prettierVersion } from "prettier";
+import { locStart } from "./loc.js";
 
 const {
   hasNewline,
@@ -605,7 +606,7 @@ function hasEmptyBody(path, name = "body") {
   );
 }
 
-function isNextLineEmptyAfterNamespace(text, node, locStart) {
+function isNextLineEmptyAfterNamespace(text, node) {
   let idx = locStart(node);
   idx = skipEverythingButNewLine(text, idx);
   idx = skipNewline(text, idx);
@@ -665,17 +666,15 @@ const magicMethods = [
   "__clone",
   "__debugInfo",
 ];
-const MagicMethodsMap = magicMethods.reduce((map, obj) => {
-  map[obj.toLowerCase()] = obj;
-
-  return map;
-}, {});
+const magicMethodsMap = new Map(
+  magicMethods.map((name) => [name.toLowerCase(), name])
+);
 
 function normalizeMagicMethodName(name) {
   const loweredName = name.toLowerCase();
 
-  if (MagicMethodsMap[loweredName]) {
-    return MagicMethodsMap[loweredName];
+  if (magicMethodsMap.has(loweredName)) {
+    return magicMethodsMap.get(loweredName);
   }
 
   return name;


Test patch:
diff --git a/tests/case/__snapshots__/jsfmt.spec.js.snap b/tests/case/__snapshots__/jsfmt.spec.js.snap
index 4a591ad..aed718b 100644
--- a/tests/case/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/case/__snapshots__/jsfmt.spec.js.snap
@@ -569,6 +569,10 @@ class Connection
     public function __DEBUGINFO()
     {
     }
+
+    // Not magic methods, JS Object prototype properties
+    public function __pRoTo__() {}
+    public function cOnStRuCtOr() {}
 }
 
 =====================================output=====================================
@@ -638,6 +642,14 @@ class Connection
     public function __debugInfo()
     {
     }
+
+    // Not magic methods, JS Object prototype properties
+    public function __pRoTo__()
+    {
+    }
+    public function cOnStRuCtOr()
+    {
+    }
 }
 
 ================================================================================
diff --git a/tests/case/magic-methods.php b/tests/case/magic-methods.php
index 16b15a7..8761bc0 100644
--- a/tests/case/magic-methods.php
+++ b/tests/case/magic-methods.php
@@ -64,4 +64,8 @@ class Connection
     public function __DEBUGINFO()
     {
     }
+
+    // Not magic methods, JS Object prototype properties
+    public function __pRoTo__() {}
+    public function cOnStRuCtOr() {}
 }