Repository: prettier/plugin-php
Pull request #127 (2018-02-26 15:10:30)

tests: void functions
Just tests


Patch:
diff --git a/src/clean.js b/src/clean.js
index edb95a6..499c673 100644
--- a/src/clean.js
+++ b/src/clean.js
@@ -16,11 +16,35 @@ function clean(ast) {
         : { kind: ast.kind, level };
     }
   }
+  // Normalize numbers
   if (ast.kind === "number") {
     ast.value = util.printNumber(ast.value);
 
     return ast;
   }
+  // All magic constant should be upper case
+  if (ast.kind === "magic") {
+    return ast.value.toUpperCase();
+  }
+
+  // All reserved words should be lowercase case
+  if (ast.kind === "identifier" && typeof ast.name === "string") {
+    const lowerCasedName = ast.name.toLowerCase();
+    const isLowerCase =
+      [
+        "int",
+        "float",
+        "bool",
+        "string",
+        "null",
+        "void",
+        "iterable",
+        "object",
+        "self"
+      ].indexOf(lowerCasedName) !== -1;
+
+    return isLowerCase ? lowerCasedName : ast.name;
+  }
 }
 
 module.exports = clean;
diff --git a/src/printer.js b/src/printer.js
index 092701e..46bbaee 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -10,6 +10,7 @@ const join = docBuilders.join;
 const line = docBuilders.line;
 const group = docBuilders.group;
 const indent = docBuilders.indent;
+const ifBreak = docBuilders.ifBreak;
 const hardline = docBuilders.hardline;
 const softline = docBuilders.softline;
 
@@ -18,6 +19,17 @@ function includes(array, val) {
   return array.indexOf(val) !== -1;
 }
 
+function shouldPrintComma(options) {
+  switch (options.trailingComma) {
+    case "all":
+      return true;
+    // fallthrough
+    case "none":
+    default:
+      return false;
+  }
+}
+
 function genericPrint(path, options, print) {
   const n = path.getValue();
   if (!n) {
@@ -258,7 +270,8 @@ function printExpression(path, options, print) {
       case "inline":
         return node.value;
       case "magic":
-        return node.value;
+        // for magic constant we prefer upper case
+        return node.value.toUpperCase();
       case "nowdoc":
         return concat([
           "<<<'",
@@ -301,6 +314,7 @@ function printExpression(path, options, print) {
               join(concat([",", line]), path.map(print, "items"))
             ])
           ),
+          ifBreak(shouldPrintComma(options) ? "," : ""),
           softline,
           node.shortForm ? "]" : ")"
         ])
@@ -1078,14 +1092,31 @@ function printNode(path, options, print) {
     return printStatement(path, options, print);
   }
   switch (node.kind) {
-    case "identifier":
+    case "identifier": {
       // this is a hack until https://github.com/glayzzle/php-parser/issues/113 is resolved
+      // for reserved words we prefer lowercase case
       if (node.name === "\\array") {
         return "array";
       } else if (node.name === "\\callable") {
         return "callable";
       }
-      return node.name;
+
+      const lowerCasedName = node.name.toLowerCase();
+      const isLowerCase =
+        [
+          "int",
+          "float",
+          "bool",
+          "string",
+          "null",
+          "void",
+          "iterable",
+          "object",
+          "self"
+        ].indexOf(lowerCasedName) !== -1;
+
+      return isLowerCase ? lowerCasedName : node.name;
+    }
     case "case":
       return concat([
         node.test
@@ -1122,6 +1153,7 @@ function printNode(path, options, print) {
       return "return";
     case "doc": {
       let canAddEmptyLine = false;
+      // @TODO: need refactor after resolve https://github.com/glayzzle/php-parser/issues/114
       return node.isDoc
         ? concat([
             "/**",
@@ -1146,7 +1178,23 @@ function printNode(path, options, print) {
             node.lines.length > 1 ? hardline : "",
             " */"
           ])
-        : join(hardline, node.lines.map(comment => concat(["// ", comment])));
+        : join(
+            hardline,
+            node.lines.map(comment =>
+              concat([
+                comment.split(/\n/).length > 1
+                  ? concat(["/*", comment[0] === "*" ? "" : " "])
+                  : "// ",
+                comment,
+                comment.split(/\n/).length > 1
+                  ? concat([
+                      comment[comment.length - 1] === "*" ? "" : " ",
+                      "*/"
+                    ])
+                  : ""
+              ])
+            )
+          );
     }
     case "entry":
       return concat([


Test patch:
diff --git a/tests/case/__snapshots__/jsfmt.spec.js.snap b/tests/case/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 0000000..5e1a06f
--- /dev/null
+++ b/tests/case/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,488 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`case.php 1`] = `
+<?php
+DECLARE(ticks=1);
+
+NAMESPACE Vendor\\Package;
+
+USE FooClass;
+USE BarClass AS Bar;
+
+INTERFACE iTemplate {}
+
+ABSTRACT CLASS AbstractClass
+{
+    ABSTRACT PROTECTED FUNCTION getValue();
+    ABSTRACT PROTECTED FUNCTION prefixValue($prefix);
+
+    PUBLIC function printOut() {
+        PRINT $this->getValue();
+    }
+}
+
+TRAIT ezcReflectionReturnInfo {
+    FUNCTION getReturnType() { /* 1 */ }
+    FUNCTION getReturnDescription() { /* 2 */ }
+}
+
+FINAL CLASS MyClass EXTENDS Bar IMPLEMENTS iTemplate
+{
+    USE A, B {
+        B::smallTalk INSTEADOF A;
+        A::bigTalk INSTEADOF B;
+        B::bigTalk AS talk;
+    }
+
+    CONST CONSTANT = 'CONSTANT';
+
+    PUBLIC $public = 'Public';
+
+    PROTECTED $protected = 'Protected';
+
+    PRIVATE $private = 'Private';
+
+    VAR $foo = 'Bar';
+
+    FUNCTION printHello()
+    {
+        ECHO $this->public;
+
+        $myclass = NEW MyClass();
+
+        $true = TRUE;
+        $false = FALSE;
+        $null = NULL;
+        $other_null = nUlL;
+        $null_string = "NULL";
+
+        IF ($expr1) {
+            // IF body
+        } ELSEIF ($expr2) {
+            // ELSEIF body
+        } ELSE IF ($expr3) {
+            // ELSE IF body
+        } ELSE {
+            // Else body
+        }
+
+        SWITCH ($expr) {
+            CASE 0:
+                ECHO 'First case, with a break';
+                BREAK;
+            CASE 1:
+            CASE 4:
+                ECHO 'Third case, return instead of break';
+                RETURN;
+            DEFAULT:
+                ECHO 'Default case';
+                BREAK;
+        }
+
+        WHILE (LIST($key, $value) = foo($arr)) {
+            if (!($key % 2)) {
+                CONTINUE;
+            }
+
+            do_something_odd($value);
+        }
+
+        DO {
+            // Structure body
+        } WHILE ($expr);
+
+        FOR ($i = 0; $i < 10; $i++) {
+            // For body
+        }
+
+        FOREACH ($iterable AS $key => $value) {
+            // Foreach body
+        }
+
+        TRY {
+            // Try body
+        } CATCH (FirstExceptionType $e) {
+            // Catch body
+        } FINALLY {
+            restore_error_handler();
+        }
+
+        $noArgs_longVars = FUNCTION () USE (
+            $longVar1,
+            $longerVar2,
+            $muchLongerVar3
+        ) {
+            // Body
+        };
+
+        $obj2 = CLONE $obj;
+
+        IF (EMPTY($var)) {
+            ECHO '$var или 0, или пусто, или вообще не определена';
+        }
+
+        IF (ISSET($var)) {
+            ECHO '$var определена, даже если она пустая';
+        }
+
+        __line__;
+        __file__;
+        __dir__;
+        __function__;
+        __class__;
+        __trait__;
+        __method__;
+        __namespace__;
+
+        $c = (FALSE AND foo());
+        $d = (TRUE OR foo());
+        $c = (FALSE XOR foo());
+
+        $array = ARRAY(1, 1, 1);
+
+        IF ($step <= 0) {
+            THROW NEW LogicException('Logic');
+        }
+
+        FOR ($i = $start; $i <= $limit; $i += $step) {
+            YIELD $i;
+        }
+
+        GLOBAL $foo;
+        UNSET($foo);
+
+        var_dump($a INSTANCEOF MyClass);
+
+        DIE();
+        EXIT();
+        EVAL('test');
+    }
+
+    PUBLIC FUNCTION MyPublic()
+    {
+        ECHO self::CONSTANT;
+    }
+
+    PROTECTED FUNCTION MyProtected()
+    {
+        REQUIRE('somefile.php');
+        REQUIRE_ONCE('somefile.php');
+        INCLUDE('somefile.php');
+        INCLUDE_ONCE('somefile.php');
+    }
+
+    PRIVATE FUNCTION MyPrivate()
+    {
+        GOTO a;
+        ECHO 'Foo';
+
+        a:
+        ECHO 'Bar';
+    }
+
+    FINAL PUBLIC STATIC FUNCTION bar(CALLABLE $callback, $coffeeMaker = NULL)
+    {
+        IF ($a == 5):
+            ECHO "5";
+        ELSEIF ($a == 6):
+            ECHO "6";
+        ELSE:
+            ECHO "other";
+        ENDIF;
+
+        $i = 1;
+
+        WHILE ($i <= 10):
+            ECHO $i;
+            $i++;
+        ENDWHILE;
+
+        FOR (;;):
+            if ($i > 10) {
+                BREAK;
+            }
+
+            ECHO $i;
+            $i++;
+        ENDFOR;
+
+        FOREACH ($array AS $element):
+            echo $element;
+        ENDFOREACH;
+
+        SWITCH ($var):
+            CASE 1:
+            CASE 2:
+                ECHO "Goodbye!";
+                BREAK;
+            DEFAULT:
+                ECHO "I only understand 1 and 2.";
+        ENDSWITCH;
+    }
+
+    PUBLIC FUNCTION method(
+        INT $param1,
+        FLOAT $param2,
+        BOOL $param3,
+        STRING $param4,
+        ITERABLE $param5,
+        OBJECT $param6,
+        ClassA $param7,
+        SELF $param8,
+        Name\\Space\\ClassA $param8
+    ): VOID
+    {
+        // Nothing
+    }
+}
+
+__HALT_COMPILER();
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<?php
+declare(ticks=1);
+namespace Vendor\\Package;
+
+use FooClass;
+use BarClass as Bar;
+
+interface iTemplate
+{
+
+}
+
+abstract class AbstractClass
+{
+  abstract protected function getValue();
+  abstract protected function prefixValue($prefix);
+
+  public function printOut()
+  {
+    print $this->getValue();
+  }
+}
+
+trait ezcReflectionReturnInfo
+{
+  public function getReturnType()
+  {
+    // 1
+  }
+  public function getReturnDescription()
+  {
+    // 2
+  }
+}
+
+final class MyClass extends Bar implements iTemplate
+{
+  use A, B {
+    B::smallTalk insteadof A;
+    A::bigTalk insteadof B;
+    B::bigTalk as talk;
+  }
+
+  public const CONSTANT = 'CONSTANT';
+
+  public $public = 'Public';
+
+  protected $protected = 'Protected';
+
+  private $private = 'Private';
+
+  public $foo = 'Bar';
+
+  public function printHello()
+  {
+    echo $this->public;
+
+    $myclass = new MyClass();
+
+    $true = true;
+    $false = false;
+    $null = null;
+    $other_null = null;
+    $null_string = "NULL";
+
+    if ($expr1) {
+      // IF body
+    } elseif ($expr2) {
+      // ELSEIF body
+    } elseif ($expr3) {
+      // ELSE IF body
+    } else {
+      // Else body
+    }
+
+    switch ($expr) {
+      case 0:
+        echo 'First case, with a break';
+        break;
+      case 1:
+      case 4:
+        echo 'Third case, return instead of break';
+        return;
+      default:
+        echo 'Default case';
+        break;
+    }
+
+    while (list($key, $value) = foo($arr)) {
+      if (!($key % 2)) {
+        continue;
+      }
+
+      do_something_odd($value);
+    }
+
+    do {
+      // Structure body
+    } while ($expr);
+
+    for ($i = 0; $i < 10; $i++) {
+      // For body
+    }
+
+    foreach ($iterable as $key => $value) {
+      // Foreach body
+    }
+
+    try {
+      // Try body
+    } catch (FirstExceptionType $e) {
+      // Catch body
+    } finally {
+      restore_error_handler();
+    }
+
+    $noArgs_longVars = function () use (
+      $longVar1,
+      $longerVar2,
+      $muchLongerVar3
+    ) {
+      // Body
+    };
+
+    $obj2 = clone $obj;
+
+    if (empty($var)) {
+      echo '$var или 0, или пусто, или вообще не определена';
+    }
+
+    if (isset($var)) {
+      echo '$var определена, даже если она пустая';
+    }
+
+    __LINE__;
+    __FILE__;
+    __DIR__;
+    __FUNCTION__;
+    __CLASS__;
+    __TRAIT__;
+    __METHOD__;
+    __NAMESPACE__;
+
+    $c = (false and foo());
+    $d = (true or foo());
+    $c = (false xor foo());
+
+    $array = array(1, 1, 1);
+
+    if ($step <= 0) {
+      throw new LogicException('Logic');
+    }
+
+    for ($i = $start; $i <= $limit; $i += $step) {
+      yield $i;
+    }
+
+    global $foo;
+    unset($foo);
+
+    var_dump($a instanceof MyClass);
+
+    exit();
+    exit();
+    eval('test');
+  }
+
+  public function MyPublic()
+  {
+    echo self::CONSTANT;
+  }
+
+  protected function MyProtected()
+  {
+    require ('somefile.php');
+    require_once ('somefile.php');
+    include ('somefile.php');
+    include_once ('somefile.php');
+  }
+
+  private function MyPrivate()
+  {
+    goto a;
+    echo 'Foo';
+
+    a:
+    echo 'Bar';
+  }
+
+  final public static function bar(callable $callback, $coffeeMaker = null)
+  {
+    if ($a == 5):
+      echo "5";
+    elseif ($a == 6):
+      echo "6";
+    else:
+      echo "other";
+    endif;
+
+    $i = 1;
+
+    while ($i <= 10):
+      echo $i;
+      $i++;
+    endwhile;
+
+    for (; ; ):
+      if ($i > 10) {
+        break;
+      }
+
+      echo $i;
+      $i++;
+    endfor;
+
+    foreach ($array as $element):
+      echo $element;
+    endforeach;
+
+    switch ($var):
+      case 1:
+      case 2:
+        echo "Goodbye!";
+        break;
+      default:
+        echo "I only understand 1 and 2.";
+    endswitch;
+  }
+
+  public function method(
+    int $param1,
+    float $param2,
+    bool $param3,
+    string $param4,
+    iterable $param5,
+    object $param6,
+    ClassA $param7,
+    self $param8,
+    Name\\Space\\ClassA $param8
+  ): void
+  {
+    // Nothing
+  }
+}
+
+__halt_compiler();
+
+`;
diff --git a/tests/case/case.php b/tests/case/case.php
new file mode 100644
index 0000000..abf739f
--- /dev/null
+++ b/tests/case/case.php
@@ -0,0 +1,236 @@
+<?php
+DECLARE(ticks=1);
+
+NAMESPACE Vendor\Package;
+
+USE FooClass;
+USE BarClass AS Bar;
+
+INTERFACE iTemplate {}
+
+ABSTRACT CLASS AbstractClass
+{
+    ABSTRACT PROTECTED FUNCTION getValue();
+    ABSTRACT PROTECTED FUNCTION prefixValue($prefix);
+
+    PUBLIC function printOut() {
+        PRINT $this->getValue();
+    }
+}
+
+TRAIT ezcReflectionReturnInfo {
+    FUNCTION getReturnType() { /* 1 */ }
+    FUNCTION getReturnDescription() { /* 2 */ }
+}
+
+FINAL CLASS MyClass EXTENDS Bar IMPLEMENTS iTemplate
+{
+    USE A, B {
+        B::smallTalk INSTEADOF A;
+        A::bigTalk INSTEADOF B;
+        B::bigTalk AS talk;
+    }
+
+    CONST CONSTANT = 'CONSTANT';
+
+    PUBLIC $public = 'Public';
+
+    PROTECTED $protected = 'Protected';
+
+    PRIVATE $private = 'Private';
+
+    VAR $foo = 'Bar';
+
+    FUNCTION printHello()
+    {
+        ECHO $this->public;
+
+        $myclass = NEW MyClass();
+
+        $true = TRUE;
+        $false = FALSE;
+        $null = NULL;
+        $other_null = nUlL;
+        $null_string = "NULL";
+
+        IF ($expr1) {
+            // IF body
+        } ELSEIF ($expr2) {
+            // ELSEIF body
+        } ELSE IF ($expr3) {
+            // ELSE IF body
+        } ELSE {
+            // Else body
+        }
+
+        SWITCH ($expr) {
+            CASE 0:
+                ECHO 'First case, with a break';
+                BREAK;
+            CASE 1:
+            CASE 4:
+                ECHO 'Third case, return instead of break';
+                RETURN;
+            DEFAULT:
+                ECHO 'Default case';
+                BREAK;
+        }
+
+        WHILE (LIST($key, $value) = foo($arr)) {
+            if (!($key % 2)) {
+                CONTINUE;
+            }
+
+            do_something_odd($value);
+        }
+
+        DO {
+            // Structure body
+        } WHILE ($expr);
+
+        FOR ($i = 0; $i < 10; $i++) {
+            // For body
+        }
+
+        FOREACH ($iterable AS $key => $value) {
+            // Foreach body
+        }
+
+        TRY {
+            // Try body
+        } CATCH (FirstExceptionType $e) {
+            // Catch body
+        } FINALLY {
+            restore_error_handler();
+        }
+
+        $noArgs_longVars = FUNCTION () USE (
+            $longVar1,
+            $longerVar2,
+            $muchLongerVar3
+        ) {
+            // Body
+        };
+
+        $obj2 = CLONE $obj;
+
+        IF (EMPTY($var)) {
+            ECHO '$var или 0, или пусто, или вообще не определена';
+        }
+
+        IF (ISSET($var)) {
+            ECHO '$var определена, даже если она пустая';
+        }
+
+        __line__;
+        __file__;
+        __dir__;
+        __function__;
+        __class__;
+        __trait__;
+        __method__;
+        __namespace__;
+
+        $c = (FALSE AND foo());
+        $d = (TRUE OR foo());
+        $c = (FALSE XOR foo());
+
+        $array = ARRAY(1, 1, 1);
+
+        IF ($step <= 0) {
+            THROW NEW LogicException('Logic');
+        }
+
+        FOR ($i = $start; $i <= $limit; $i += $step) {
+            YIELD $i;
+        }
+
+        GLOBAL $foo;
+        UNSET($foo);
+
+        var_dump($a INSTANCEOF MyClass);
+
+        DIE();
+        EXIT();
+        EVAL('test');
+    }
+
+    PUBLIC FUNCTION MyPublic()
+    {
+        ECHO self::CONSTANT;
+    }
+
+    PROTECTED FUNCTION MyProtected()
+    {
+        REQUIRE('somefile.php');
+        REQUIRE_ONCE('somefile.php');
+        INCLUDE('somefile.php');
+        INCLUDE_ONCE('somefile.php');
+    }
+
+    PRIVATE FUNCTION MyPrivate()
+    {
+        GOTO a;
+        ECHO 'Foo';
+
+        a:
+        ECHO 'Bar';
+    }
+
+    FINAL PUBLIC STATIC FUNCTION bar(CALLABLE $callback, $coffeeMaker = NULL)
+    {
+        IF ($a == 5):
+            ECHO "5";
+        ELSEIF ($a == 6):
+            ECHO "6";
+        ELSE:
+            ECHO "other";
+        ENDIF;
+
+        $i = 1;
+
+        WHILE ($i <= 10):
+            ECHO $i;
+            $i++;
+        ENDWHILE;
+
+        FOR (;;):
+            if ($i > 10) {
+                BREAK;
+            }
+
+            ECHO $i;
+            $i++;
+        ENDFOR;
+
+        FOREACH ($array AS $element):
+            echo $element;
+        ENDFOREACH;
+
+        SWITCH ($var):
+            CASE 1:
+            CASE 2:
+                ECHO "Goodbye!";
+                BREAK;
+            DEFAULT:
+                ECHO "I only understand 1 and 2.";
+        ENDSWITCH;
+    }
+
+    PUBLIC FUNCTION method(
+        INT $param1,
+        FLOAT $param2,
+        BOOL $param3,
+        STRING $param4,
+        ITERABLE $param5,
+        OBJECT $param6,
+        ClassA $param7,
+        SELF $param8,
+        Name\Space\ClassA $param8
+    ): VOID
+    {
+        // Nothing
+    }
+}
+
+__HALT_COMPILER();
diff --git a/tests/case/jsfmt.spec.js b/tests/case/jsfmt.spec.js
new file mode 100644
index 0000000..e395434
--- /dev/null
+++ b/tests/case/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["php"]);
diff --git a/tests/class/__snapshots__/jsfmt.spec.js.snap b/tests/class/__snapshots__/jsfmt.spec.js.snap
index 8fe7c6c..bf80fa0 100644
--- a/tests/class/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/class/__snapshots__/jsfmt.spec.js.snap
@@ -164,6 +164,17 @@ abstract class ReallyReallyReallyLongClassName extends AbstractModelFactoryResou
   {
         return $name;
   }
+
+  public function swap(&$left, &$right): void
+  {
+      if ($left === $right) {
+          return;
+      }
+
+      $tmp = $left;
+      $left = $right;
+      $right = $tmp;
+  }
 }
 
 $this->something->method($argument, $this->more->stuff($this->even->more->things->complicatedMethod()));
@@ -304,6 +315,17 @@ abstract class ReallyReallyReallyLongClassName
   {
     return $name;
   }
+
+  public function swap(&$left, &$right): void
+  {
+    if ($left === $right) {
+      return;
+    }
+
+    $tmp = $left;
+    $left = $right;
+    $right = $tmp;
+  }
 }
 
 $this->something->method(
diff --git a/tests/class/class.php b/tests/class/class.php
index 813ba92..819ef0f 100644
--- a/tests/class/class.php
+++ b/tests/class/class.php
@@ -102,6 +102,17 @@ abstract class ReallyReallyReallyLongClassName extends AbstractModelFactoryResou
   {
         return $name;
   }
+
+  public function swap(&$left, &$right): void
+  {
+      if ($left === $right) {
+          return;
+      }
+
+      $tmp = $left;
+      $left = $right;
+      $right = $tmp;
+  }
 }
 
 $this->something->method($argument, $this->more->stuff($this->even->more->things->complicatedMethod()));
diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap
index 1bf9586..b59d5c7 100644
--- a/tests/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap
@@ -45,6 +45,23 @@ class Foo
         $this->title = $title;
     }
 }
+
+/*********************
+ * Some long comment *
+ *********************/
+
+// This is a one-line c++ style comment
+
+/* This is a multi line comment
+   yet another line of comment */
+
+/*
+This is a multiple-lines comment block
+that spans over multiple
+lines
+*/
+
+# This is a one-line shell-style comment
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 <?php
 /** @var int $int This is a counter. */
@@ -91,4 +108,15 @@ class Foo
   }
 }
 
+/*********************
+ * Some long comment *
+ *********************/
+// This is a one-line c++ style comment
+/* This is a multi line comment
+   yet another line of comment */
+/* This is a multiple-lines comment block
+that spans over multiple
+lines */
+// This is a one-line shell-style comment
+
 `;
diff --git a/tests/comments/comments.php b/tests/comments/comments.php
index 9dd88b9..0e74a4d 100644
--- a/tests/comments/comments.php
+++ b/tests/comments/comments.php
@@ -42,3 +42,20 @@ class Foo
         $this->title = $title;
     }
 }
+
+/*********************
+ * Some long comment *
+ *********************/
+
+// This is a one-line c++ style comment
+
+/* This is a multi line comment
+   yet another line of comment */
+
+/*
+This is a multiple-lines comment block
+that spans over multiple
+lines
+*/
+
+# This is a one-line shell-style comment
diff --git a/tests/functions/__snapshots__/jsfmt.spec.js.snap b/tests/functions/__snapshots__/jsfmt.spec.js.snap
index bb56c8e..5d51032 100644
--- a/tests/functions/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/functions/__snapshots__/jsfmt.spec.js.snap
@@ -74,6 +74,17 @@ function testReturn(?string $name): ?string
 {
     return $name;
 }
+
+function swap(&$left, &$right): void
+{
+    if ($left === $right) {
+        return;
+    }
+
+    $tmp = $left;
+    $left = $right;
+    $right = $tmp;
+}
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 <?php
 function hello()
@@ -180,4 +191,15 @@ function testReturn(?string $name): ?string
   return $name;
 }
 
+function swap(&$left, &$right): void
+{
+  if ($left === $right) {
+    return;
+  }
+
+  $tmp = $left;
+  $left = $right;
+  $right = $tmp;
+}
+
 `;
diff --git a/tests/functions/functions.php b/tests/functions/functions.php
index 834eb27..ad02b8c 100644
--- a/tests/functions/functions.php
+++ b/tests/functions/functions.php
@@ -71,3 +71,14 @@ function testReturn(?string $name): ?string
 {
     return $name;
 }
+
+function swap(&$left, &$right): void
+{
+    if ($left === $right) {
+        return;
+    }
+
+    $tmp = $left;
+    $left = $right;
+    $right = $tmp;
+}
diff --git a/tests/trailing_commas/__snapshots__/jsfmt.spec.js.snap b/tests/trailing_commas/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 0000000..10c7083
--- /dev/null
+++ b/tests/trailing_commas/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,113 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`trailing_commas.php 1`] = `
+<?php
+
+$foo = ['foo', 'bar'];
+$foo = ['foo', 'bar',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',];
+$foo = array('foo', 'bar');
+$foo = array('foo', 'bar',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<?php
+$foo = ['foo', 'bar'];
+$foo = ['foo', 'bar'];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2'
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2'
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'
+];
+$foo = array('foo', 'bar');
+$foo = array('foo', 'bar');
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2'
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2'
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'
+);
+
+`;
+
+exports[`trailing_commas.php 2`] = `
+<?php
+
+$foo = ['foo', 'bar'];
+$foo = ['foo', 'bar',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',];
+$foo = array('foo', 'bar');
+$foo = array('foo', 'bar',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<?php
+$foo = ['foo', 'bar'];
+$foo = ['foo', 'bar'];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2',
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2',
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',
+];
+$foo = [
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',
+];
+$foo = array('foo', 'bar');
+$foo = array('foo', 'bar');
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2',
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1',
+  'very-very-very-very-very-very-very-very-very-long-item-2',
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',
+);
+$foo = array(
+  'very-very-very-very-very-very-very-very-very-long-item-1' => 'foo',
+  'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',
+);
+
+`;
diff --git a/tests/trailing_commas/jsfmt.spec.js b/tests/trailing_commas/jsfmt.spec.js
new file mode 100644
index 0000000..c2fbddb
--- /dev/null
+++ b/tests/trailing_commas/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["php"]);
+run_spec(__dirname, ["php"], { trailingComma: "all" });
diff --git a/tests/trailing_commas/trailing_commas.php b/tests/trailing_commas/trailing_commas.php
new file mode 100644
index 0000000..3d0f296
--- /dev/null
+++ b/tests/trailing_commas/trailing_commas.php
@@ -0,0 +1,14 @@
+<?php
+
+$foo = ['foo', 'bar'];
+$foo = ['foo', 'bar',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar'];
+$foo = ['very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',];
+$foo = array('foo', 'bar');
+$foo = array('foo', 'bar',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1', 'very-very-very-very-very-very-very-very-very-long-item-2',);
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar');
+$foo = array('very-very-very-very-very-very-very-very-very-long-item-1' => 'foo', 'very-very-very-very-very-very-very-very-very-long-item-2' => 'bar',);