whole_func_string: function() {
      if (this.is("T_MAGIC_CONST")) {
        return this.get_magic_constant();
      } else {
        let value, node;
        switch (this.token) {
          // TEXTS
          case this.tok.T_CONSTANT_ENCAPSED_STRING: {
            value = this.node("string");
            const text = this.text();
            let offset = 0;
            if (text[0] === "b" || text[0] === "B") {
              offset = 1;
            }
            const isDoubleQuote = text[offset] === '"';
            this.next();
            value = value(
              isDoubleQuote,
              this.resolve_special_chars(
                text.substring(offset + 1, text.length - 1),
                isDoubleQuote
              ),
              offset === 1, // unicode flag
              text
            );
            if (this.token === this.tok.T_DOUBLE_COLON) {
              // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1151
              return this.read_static_getter(value);
            } else {
              // dirrect string
              return value;
            }
          }
          case this.tok.T_START_HEREDOC:
            if (this.lexer.curCondition === "ST_NOWDOC") {
              const start = this.lexer.yylloc.first_offset;
              node = this.node("nowdoc");
              value = this.next().text();
              // strip the last line return char
              const lastCh = value[value.length - 1];
              if (lastCh === "\n") {
                if (value[value.length - 2] === "\r") {
                  // windows style
                  value = value.substring(0, value.length - 2);
                } else {
                  // linux style
                  value = value.substring(0, value.length - 1);
                }
              } else if (lastCh === "\r") {
                // mac style
                value = value.substring(0, value.length - 1);
              }
              this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE) && this.next();
              const raw = this.lexer._input.substring(
                start,
                this.lexer.yylloc.last_offset
              );
              this.expect(this.tok.T_END_HEREDOC) && this.next();
              node = node(
                value,
                raw,
                this.lexer.heredoc_label,
                raw[3] === '"' || raw[3] === "'"
              );
              return node;
            } else {
              return this.read_encapsed_string(this.tok.T_END_HEREDOC);
            }
  
          case '"':
            return this.read_encapsed_string('"');
  
          case 'b"':
          case 'B"': {
            return this.read_encapsed_string('"', true);
          }
  
          // NUMERIC
          case this.tok.T_LNUMBER: // long
          case this.tok.T_DNUMBER: {
            // double
            const result = this.node("number");
            value = this.text();
            this.next();
            return result(value, null);
          }
  
          // ARRAYS
          case this.tok.T_ARRAY: // array parser
            return this.read_array();
          case "[": // short array format
            return this.read_array();
          default: {
            const err = this.error("SCALAR");
            // graceful mode : ignore token & return error node
            this.next();
            return err;
          }
        }
      }
    }
func_code_string: function() {
      if (this.is("T_MAGIC_CONST")) {
        return this.get_magic_constant();
      } else {
        let value, node;
        switch (this.token) {
          // TEXTS
          case this.tok.T_CONSTANT_ENCAPSED_STRING: {
            value = this.node("string");
            const text = this.text();
            let offset = 0;
            if (text[0] === "b" || text[0] === "B") {
              offset = 1;
            }
            const isDoubleQuote = text[offset] === '"';
            this.next();
            value = value(
              isDoubleQuote,
              this.resolve_special_chars(
                text.substring(offset + 1, text.length - 1),
                isDoubleQuote
              ),
              offset === 1, // unicode flag
              text
            );
            if (this.token === this.tok.T_DOUBLE_COLON) {
              // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1151
              return this.read_static_getter(value);
            } else {
              // dirrect string
              return value;
            }
          }
          case this.tok.T_START_HEREDOC:
            if (this.lexer.curCondition === "ST_NOWDOC") {
              const start = this.lexer.yylloc.first_offset;
              node = this.node("nowdoc");
              value = this.next().text();
              // strip the last line return char
              const lastCh = value[value.length - 1];
              if (lastCh === "\n") {
                if (value[value.length - 2] === "\r") {
                  // windows style
                  value = value.substring(0, value.length - 2);
                } else {
                  // linux style
                  value = value.substring(0, value.length - 1);
                }
              } else if (lastCh === "\r") {
                // mac style
                value = value.substring(0, value.length - 1);
              }
              this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE) && this.next();
              const raw = this.lexer._input.substring(
                start,
                this.lexer.yylloc.last_offset
              );
              this.expect(this.tok.T_END_HEREDOC) && this.next();
              node = node(
                value,
                raw,
                this.lexer.heredoc_label,
                raw[3] === '"' || raw[3] === "'"
              );
              return node;
            } else {
              return this.read_encapsed_string(this.tok.T_END_HEREDOC);
            }
  
          case '"':
            return this.read_encapsed_string('"');
  
          case 'b"':
          case 'B"': {
            return this.read_encapsed_string('"', true);
          }
  
          // NUMERIC
          case this.tok.T_LNUMBER: // long
          case this.tok.T_DNUMBER: {
            // double
            const result = this.node("number");
            value = this.text();
            this.next();
            return result(value, null);
          }
  
          // ARRAYS
          case this.tok.T_ARRAY: // array parser
            return this.read_array();
          case "[": // short array format
            return this.read_array();
          default: {
            const err = this.error("SCALAR");
            // graceful mode : ignore token & return error node
            this.next();
            return err;
          }
        }
      }
    }
func_documentation_string: ```ebnf
  scalar ::= T_MAGIC_CONST
  | T_LNUMBER | T_DNUMBER
  | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE? T_END_HEREDOC
  | '"' encaps_list '"'
  | T_START_HEREDOC encaps_list T_END_HEREDOC
  | namespace_name (T_DOUBLE_COLON T_STRING)?
  ```