Repository: phpactor/phpactor (php)
Issue: the quick fix 'Extract constant' does not support heredoc strings
Hi,

It looks like the quick fix action 'Extract constant' does not support heredoc strings properly.

Here is a reproducer:

```php
<?php

declare(strict_types=1);

final class MyClass
{
    public function __invoke(): void
    {
        $string = <<<EOT
        hello
        EOT;

        echo $string;
    }
}
```

Hit the 'Extract constant' quick fix, and you get this code:

```php
<?php

declare(strict_types=1);

final class MyClass
{
    const NEW_CONSTANT = '';

    public function __invoke(): void
    {
        $string = self::NEW_CONSTANT;

        echo $string;
    }
}
```

Context: branch 'master' of phpactor, used by vscode-phpactor, PHP 8.1.11.
Pull request description: Fixing the string resolver
The string resolver didn't handle the case where the string literal was a heredoc string.

Fixes #1969

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 310cc534..1990ba8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@ Bug fixes:
     support it #1951
   - [lsp] Fix highlighting on PHP 8.1 #1960
   - [wr] Do not crash when encountering an array union operator #1971 @wouterj
+  - [wr] Fixing handling of HEREDOC in StringResolver #1977 @mamazu
 
 ## 2022.11.12
 
@@ -179,7 +180,7 @@ Features:
   - [cmp] Explicitly enable/disable completors and disable `keyword` completor by default.
   - [wr] Support `iterator_to_array`
   - [wr] Handle constant glob to union types (`@return Foo::BAR_*`).
-  - [lsp] show class category in offset hover info 
+  - [lsp] show class category in offset hover info
   - [lsp] jump to types in a union type
   - [wr] Type combination
   - [wr] Support for type assertions via. is_*, instanceof etc
@@ -317,7 +318,7 @@ Improvements:
 
   - [worse-reflection] Support for virtual methods in interfaces - @dantleech
   - [code-transform] Fix regression with importing from root namespace
- 
+
 ## 2020-06-09 (0.16.0)
 
 Features:
@@ -343,7 +344,7 @@ Improvements:
   - [language-server-bridge] Service to convert Phpactor Locations to LSP locations - @dantleech
   - [code-transform] Class import updates context name on alias - @dantleech
   - [documentation] Generate the configuration reference - @dantleech
-  - [completion-worse] Allow completors to be disabled via `completion_worse.disabled_completors` - @dantleech 
+  - [completion-worse] Allow completors to be disabled via `completion_worse.disabled_completors` - @dantleech
   - [indexer-extension] Validate search results (remove from search index if invalid).
   - [language-server] Exit session immediately if NULL given as CWD (instead of crashing).
   - [container] Adds command for introspecting the container (`container:dump`) - @dantleech
@@ -375,7 +376,7 @@ Features:
     processes) (#929) - @dantleech
   - [language-server] Included in the core - @dantleech
   - [indexer] Indexer included in the core - @dantleech
-  - [rpc] Add docblock prose to hover 
+  - [rpc] Add docblock prose to hover
   - [vim-plugin] Add support `:checkhealth` and provide `:PhpactorStatus` in
     terminal window (#974) - @elythyr
   - [ref-finder] Goto definition works for vars (https://github.com/phpactor/worse-reference-finder/pull/1) - @FatBoyXPC
@@ -785,7 +786,7 @@ Improvements:
     instead defaulting to either composer or full-filesystem search depending
     on env).
   - [ClassMover] (RPC) Will update current (unsaved) source.
-  - [vim-plugin] Correctly handle expanding class when at beginning of word, #438 thanks @greg0ire 
+  - [vim-plugin] Correctly handle expanding class when at beginning of word, #438 thanks @greg0ire
   - [vim-plugin] Reload file before replacing contents, fixes #445
   - [vim-plugin] File references, do not show quick fix list if all references
     are in current file.
@@ -885,7 +886,7 @@ Bugfixes:
    - [VimPlugin] Fixed goto definition, #398
    - [WorseReflection] [problem with name import](https://github.com/phpactor/worse-reflection/pull/37) (thanks @adeslade)
 
-## 0.1.0 
+## 0.1.0
 
 **2018-04-03**
 
diff --git a/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php b/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php
index b786f121..0299c31f 100644
--- a/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php
+++ b/lib/WorseReflection/Core/Inference/Resolver/StringLiteralResolver.php
@@ -36,17 +36,20 @@ class StringLiteralResolver implements Resolver
     private function getStringContentsText(StringLiteral $node): string
     {
         $children = $node->children;
+        if (is_array($children) && array_key_exists(0, $children)) {
+            $children = $children[0];
+        }
+
         if ($children instanceof Token) {
             $value = (string)$children->getText($node->getFileContents());
             $startQuote = substr($node, 0, 1);
 
-            if ($startQuote === '\'') {
-                return rtrim(substr($value, 1), '\'');
-            }
-
-            if ($startQuote === '"') {
-                return rtrim(substr($value, 1), '"');
-            }
+            return match ($startQuote) {
+                '\'' => rtrim(substr($value, 1), '\''),
+                '"' => rtrim(substr($value, 1), '"'),
+                '<' => trim($value),
+                default => ''
+            };
         }
 
         return '';


Test patch:
diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php
index b0b97ccf..9fba8bf8 100644
--- a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php
+++ b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/WorseExtractConstantTest.php
@@ -54,6 +54,10 @@ class WorseExtractConstantTest extends WorseTestCase
                 'extractConstant6.test',
                 'HOUR'
             ],
+            'replace heredoc' => [
+                'extractConstant7.test',
+                'HELLO_WORLD'
+            ],
         ];
     }
 
diff --git a/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant7.test b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant7.test
new file mode 100644
index 00000000..9a3cecaa
--- /dev/null
+++ b/lib/CodeTransform/Tests/Adapter/WorseReflection/Refactor/fixtures/extractConstant7.test
@@ -0,0 +1,24 @@
+// File: source
+<?php
+
+class extractMethod1
+{
+    public function bigMethod()
+    {
+        echo <<<EOT
+<>I am a HEREDOC
+EOT;
+    }
+}
+// File: expected
+<?php
+
+class extractMethod1
+{
+    const HELLO_WORLD = 'I am a HEREDOC';
+
+    public function bigMethod()
+    {
+        echo self::HELLO_WORLD;
+    }
+}