| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 | <?php declare(strict_types=1);namespace PhpParser;class Comment implements \JsonSerializable{    protected $text;    protected $startLine;    protected $startFilePos;    protected $startTokenPos;    protected $endLine;    protected $endFilePos;    protected $endTokenPos;    /**     * Constructs a comment node.     *     * @param string $text          Comment text (including comment delimiters like /*)     * @param int    $startLine     Line number the comment started on     * @param int    $startFilePos  File offset the comment started on     * @param int    $startTokenPos Token offset the comment started on     */    public function __construct(        string $text,        int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,        int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1    ) {        $this->text = $text;        $this->startLine = $startLine;        $this->startFilePos = $startFilePos;        $this->startTokenPos = $startTokenPos;        $this->endLine = $endLine;        $this->endFilePos = $endFilePos;        $this->endTokenPos = $endTokenPos;    }    /**     * Gets the comment text.     *     * @return string The comment text (including comment delimiters like /*)     */    public function getText() : string {        return $this->text;    }    /**     * Gets the line number the comment started on.     *     * @return int Line number (or -1 if not available)     */    public function getStartLine() : int {        return $this->startLine;    }    /**     * Gets the file offset the comment started on.     *     * @return int File offset (or -1 if not available)     */    public function getStartFilePos() : int {        return $this->startFilePos;    }    /**     * Gets the token offset the comment started on.     *     * @return int Token offset (or -1 if not available)     */    public function getStartTokenPos() : int {        return $this->startTokenPos;    }    /**     * Gets the line number the comment ends on.     *     * @return int Line number (or -1 if not available)     */    public function getEndLine() : int {        return $this->endLine;    }    /**     * Gets the file offset the comment ends on.     *     * @return int File offset (or -1 if not available)     */    public function getEndFilePos() : int {        return $this->endFilePos;    }    /**     * Gets the token offset the comment ends on.     *     * @return int Token offset (or -1 if not available)     */    public function getEndTokenPos() : int {        return $this->endTokenPos;    }    /**     * Gets the line number the comment started on.     *     * @deprecated Use getStartLine() instead     *     * @return int Line number     */    public function getLine() : int {        return $this->startLine;    }    /**     * Gets the file offset the comment started on.     *     * @deprecated Use getStartFilePos() instead     *     * @return int File offset     */    public function getFilePos() : int {        return $this->startFilePos;    }    /**     * Gets the token offset the comment started on.     *     * @deprecated Use getStartTokenPos() instead     *     * @return int Token offset     */    public function getTokenPos() : int {        return $this->startTokenPos;    }    /**     * Gets the comment text.     *     * @return string The comment text (including comment delimiters like /*)     */    public function __toString() : string {        return $this->text;    }    /**     * Gets the reformatted comment text.     *     * "Reformatted" here means that we try to clean up the whitespace at the     * starts of the lines. This is necessary because we receive the comments     * without trailing whitespace on the first line, but with trailing whitespace     * on all subsequent lines.     *     * @return mixed|string     */    public function getReformattedText() {        $text = trim($this->text);        $newlinePos = strpos($text, "\n");        if (false === $newlinePos) {            // Single line comments don't need further processing            return $text;        } elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) {            // Multi line comment of the type            //            //     /*            //      * Some text.            //      * Some more text.            //      */            //            // is handled by replacing the whitespace sequences before the * by a single space            return preg_replace('(^\s+\*)m', ' *', $this->text);        } elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {            // Multi line comment of the type            //            //    /*            //        Some text.            //        Some more text.            //    */            //            // is handled by removing the whitespace sequence on the line before the closing            // */ on all lines. So if the last line is "    */", then "    " is removed at the            // start of all lines.            return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);        } elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {            // Multi line comment of the type            //            //     /* Some text.            //        Some more text.            //          Indented text.            //        Even more text. */            //            // is handled by removing the difference between the shortest whitespace prefix on all            // lines and the length of the "/* " opening sequence.            $prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1));            $removeLen = $prefixLen - strlen($matches[0]);            return preg_replace('(^\s{' . $removeLen . '})m', '', $text);        }        // No idea how to format this comment, so simply return as is        return $text;    }    /**     * Get length of shortest whitespace prefix (at the start of a line).     *     * If there is a line with no prefix whitespace, 0 is a valid return value.     *     * @param string $str String to check     * @return int Length in characters. Tabs count as single characters.     */    private function getShortestWhitespacePrefixLen(string $str) : int {        $lines = explode("\n", $str);        $shortestPrefixLen = \INF;        foreach ($lines as $line) {            preg_match('(^\s*)', $line, $matches);            $prefixLen = strlen($matches[0]);            if ($prefixLen < $shortestPrefixLen) {                $shortestPrefixLen = $prefixLen;            }        }        return $shortestPrefixLen;    }    /**     * @return       array     * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}     */    public function jsonSerialize() : array {        // Technically not a node, but we make it look like one anyway        $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment';        return [            'nodeType' => $type,            'text' => $this->text,            // TODO: Rename these to include "start".            'line' => $this->startLine,            'filePos' => $this->startFilePos,            'tokenPos' => $this->startTokenPos,            'endLine' => $this->endLine,            'endFilePos' => $this->endFilePos,            'endTokenPos' => $this->endTokenPos,        ];    }}
 |