whole_func_string: function() {
      const revert = this.offset;
      if (
        this._input[this.offset - 1] === "<" &&
        this._input[this.offset] === "<" &&
        this._input[this.offset + 1] === "<"
      ) {
        this.offset += 3;
  
        // optional tabs / spaces
        if (this.is_TABSPACE()) {
          while (this.offset < this.size) {
            this.offset++;
            if (!this.is_TABSPACE()) {
              break;
            }
          }
        }
  
        // optional quotes
        let tChar = this._input[this.offset - 1];
        if (tChar === "'" || tChar === '"') {
          this.offset++;
        } else {
          tChar = null;
        }
  
        // required label
        if (this.is_LABEL_START()) {
          let yyoffset = this.offset - 1;
          while (this.offset < this.size) {
            this.offset++;
            if (!this.is_LABEL()) {
              break;
            }
          }
          const yylabel = this._input.substring(yyoffset, this.offset - 1);
          if (!tChar || tChar === this._input[this.offset - 1]) {
            // required ending quote
            if (tChar) this.offset++;
            // require newline
            if (
              this._input[this.offset - 1] === "\r" ||
              this._input[this.offset - 1] === "\n"
            ) {
              // go go go
              this.heredoc_label = yylabel;
              yyoffset = this.offset - revert;
              this.offset = revert;
              this.consume(yyoffset);
              if (tChar === "'") {
                this.begin("ST_NOWDOC");
              } else {
                this.begin("ST_HEREDOC");
              }
              return this.tok.T_START_HEREDOC;
            }
          }
        }
      }
      this.offset = revert;
      return false;
    }
func_code_string: function() {
      const revert = this.offset;
      if (
        this._input[this.offset - 1] === "<" &&
        this._input[this.offset] === "<" &&
        this._input[this.offset + 1] === "<"
      ) {
        this.offset += 3;
  
        // optional tabs / spaces
        if (this.is_TABSPACE()) {
          while (this.offset < this.size) {
            this.offset++;
            if (!this.is_TABSPACE()) {
              break;
            }
          }
        }
  
        // optional quotes
        let tChar = this._input[this.offset - 1];
        if (tChar === "'" || tChar === '"') {
          this.offset++;
        } else {
          tChar = null;
        }
  
        // required label
        if (this.is_LABEL_START()) {
          let yyoffset = this.offset - 1;
          while (this.offset < this.size) {
            this.offset++;
            if (!this.is_LABEL()) {
              break;
            }
          }
          const yylabel = this._input.substring(yyoffset, this.offset - 1);
          if (!tChar || tChar === this._input[this.offset - 1]) {
            // required ending quote
            if (tChar) this.offset++;
            // require newline
            if (
              this._input[this.offset - 1] === "\r" ||
              this._input[this.offset - 1] === "\n"
            ) {
              // go go go
              this.heredoc_label = yylabel;
              yyoffset = this.offset - revert;
              this.offset = revert;
              this.consume(yyoffset);
              if (tChar === "'") {
                this.begin("ST_NOWDOC");
              } else {
                this.begin("ST_HEREDOC");
              }
              return this.tok.T_START_HEREDOC;
            }
          }
        }
      }
      this.offset = revert;
      return false;
    }
func_documentation_string: check if matching a HEREDOC state