├── .gitattributes ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── Makefile ├── README.md ├── cmd └── php-parser │ └── main.go ├── go.mod ├── go.sum ├── internal ├── php5 │ ├── node.go │ ├── parser.go │ ├── parser_test.go │ ├── php5.go │ ├── php5.y │ ├── php5_bench_test.go │ └── test.php ├── php7 │ ├── node.go │ ├── parser.go │ ├── parser_test.go │ ├── php7.go │ ├── php7.y │ ├── php7_bench_test.go │ └── test.php ├── position │ ├── position.go │ └── position_test.go └── scanner │ ├── lexer.go │ ├── newline.go │ ├── scanner.go │ ├── scanner.rl │ └── scanner_test.go ├── parser.jpg └── pkg ├── ast ├── ast.go └── node.go ├── conf └── conf.go ├── errors ├── error.go └── error_test.go ├── parser ├── doc.go └── parser.go ├── position ├── pool.go └── position.go ├── token ├── pool.go ├── pool_bench_test.go ├── token.go └── token_string.go ├── version ├── version.go └── version_test.go └── visitor ├── dumper ├── dumper.go └── dumper_test.go ├── formatter ├── formatter.go └── formatter_test.go ├── nsresolver ├── namespace_resolver.go └── namespace_resolver_test.go ├── null.go ├── printer ├── printer.go ├── printer_php5_test.go ├── printer_php7_test.go └── printer_test.go └── traverser └── traverser.go /.gitattributes: -------------------------------------------------------------------------------- 1 | internal/php5/php5.go -diff -merge 2 | internal/php5/php5.go linguist-generated=true 3 | internal/php7/php7.go -diff -merge 4 | internal/php7/php7.go linguist-generated=true 5 | internal/scanner/scanner.go -diff -merge 6 | internal/scanner/scanner.go linguist-generated=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | php-parser 4 | **/*.test 5 | 6 | *example.php 7 | 8 | cpu.pprof 9 | mem.pprof 10 | trace.out -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | branches: 3 | only: 4 | - master 5 | before_script: 6 | - env GO111MODULE=on go mod download 7 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 8 | - chmod +x ./cc-test-reporter 9 | - ./cc-test-reporter before-build 10 | script: 11 | - env GO111MODULE=on go test -coverprofile c.out ./... 12 | after_script: 13 | - ./cc-test-reporter after-build --coverage-input-type gocov --exit-code $TRAVIS_TEST_RESULT -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at z7zmey@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Slizov Vadim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PHPFILE=example.php 2 | 3 | all: compile fmt build 4 | 5 | fmt: 6 | find . -type f -iregex '.*\.go' -exec gofmt -l -s -w '{}' + 7 | 8 | build: 9 | go generate ./... 10 | go build ./cmd/... 11 | 12 | test: 13 | go test ./... 14 | 15 | cover: 16 | go test ./... --cover 17 | 18 | bench: 19 | go test -benchmem -bench=. ./internal/php5 20 | go test -benchmem -bench=. ./internal/php7 21 | 22 | compile: ./internal/php5/php5.go ./internal/php7/php7.go ./internal/scanner/scanner.go 23 | sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php7/php7.go 24 | sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php5/php5.go 25 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php5/php5.go 26 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php7/php7.go 27 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/scanner/scanner.go 28 | rm -f y.output 29 | 30 | ./internal/scanner/scanner.go: ./internal/scanner/scanner.rl 31 | ragel -Z -G2 -o $@ $< 32 | 33 | ./internal/php5/php5.go: ./internal/php5/php5.y 34 | goyacc -o $@ $< 35 | 36 | ./internal/php7/php7.go: ./internal/php7/php7.y 37 | goyacc -o $@ $< 38 | 39 | cpu_pprof: 40 | go test -cpuprofile cpu.pprof -bench=. -benchtime=20s ./internal/php7 41 | go tool pprof ./php7.test cpu.pprof 42 | 43 | mem_pprof: 44 | go test -memprofile mem.pprof -bench=. -benchtime=20s -benchmem ./internal/php7 45 | go tool pprof -alloc_objects ./php7.test mem.pprof 46 | 47 | cpu_pprof_php5: 48 | go test -cpuprofile cpu.prof -bench=. -benchtime=20s ./internal/php5 49 | go tool pprof ./php5.test cpu.prof 50 | 51 | mem_pprof_php5: 52 | go test -memprofile mem.prof -bench=. -benchtime=20s -benchmem ./internal/php5 53 | go tool pprof -alloc_objects ./php5.test mem.prof 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP Parser written in Go 2 | ======================== 3 | 4 | PHP Parser written in Go 5 | 6 | [![GoDoc](https://godoc.org/github.com/z7zmey/php-parser?status.svg)](https://godoc.org/github.com/z7zmey/php-parser) 7 | [![Build Status](https://travis-ci.org/z7zmey/php-parser.svg?branch=master)](https://travis-ci.org/z7zmey/php-parser) 8 | [![Go Report Card](https://goreportcard.com/badge/github.com/z7zmey/php-parser)](https://goreportcard.com/report/github.com/z7zmey/php-parser) 9 | [![Maintainability](https://api.codeclimate.com/v1/badges/950783b2e739db26e0ed/maintainability)](https://codeclimate.com/github/z7zmey/php-parser/maintainability) 10 | [![Test Coverage](https://api.codeclimate.com/v1/badges/950783b2e739db26e0ed/test_coverage)](https://codeclimate.com/github/z7zmey/php-parser/test_coverage) 11 | 12 | This project uses [goyacc](https://godoc.org/golang.org/x/tools/cmd/goyacc) and [ragel](https://www.colm.net/open-source/ragel/) tools to create PHP parser. It parses source code into [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree). It can be used to write static analysis, refactoring, metrics, code style formatting tools. 13 | 14 | #### Try it online: [demo](https://php-parser.com) 15 | 16 | Features: 17 | --------- 18 | 19 | - Fully support PHP 5 and PHP 7 syntax 20 | - Abstract syntax tree (AST) representation 21 | - Traversing AST 22 | - Resolving namespaced names 23 | - Parsing syntax-invalid PHP files 24 | - Saving and printing free-floating comments and whitespaces 25 | 26 | Who Uses 27 | -------- 28 | 29 | [VKCOM/noverify](https://github.com/VKCOM/noverify) - NoVerify is a pretty fast linter for PHP 30 | 31 | [quasilyte/phpgrep](https://github.com/quasilyte/phpgrep) - phpgrep is a tool for syntax-aware PHP code search 32 | 33 | Usage example 34 | ------- 35 | 36 | ```Golang 37 | package main 38 | 39 | import ( 40 | "log" 41 | "os" 42 | 43 | "github.com/z7zmey/php-parser/pkg/cfg" 44 | "github.com/z7zmey/php-parser/pkg/errors" 45 | "github.com/z7zmey/php-parser/pkg/parser" 46 | "github.com/z7zmey/php-parser/pkg/version" 47 | "github.com/z7zmey/php-parser/pkg/visitor/dumper" 48 | ) 49 | 50 | func main() { 51 | src := []byte(` ... 99 | ``` 100 | 101 | | flag | type | description | 102 | | ------- | ------ | --------------------------------- | 103 | | -p | bool | print filepath | 104 | | -e | bool | print errors | 105 | | -d | bool | dump in golang format | 106 | | -r | bool | resolve names | 107 | | -prof | string | start profiler: [cpu, mem, trace] | 108 | | -phpver | string | php version (default: 7.4) | 109 | 110 | Namespace resolver 111 | ------------------ 112 | 113 | Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[node.Node]string` structure 114 | 115 | - For `Class`, `Interface`, `Trait`, `Function`, `Constant` nodes it saves name with current namespace. 116 | - For `Name`, `Relative`, `FullyQualified` nodes it resolves `use` aliases and saves a fully qualified name. 117 | -------------------------------------------------------------------------------- /cmd/php-parser/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "runtime" 13 | "strconv" 14 | "sync" 15 | "time" 16 | 17 | "github.com/pkg/profile" 18 | "github.com/yookoala/realpath" 19 | 20 | "github.com/z7zmey/php-parser/pkg/ast" 21 | "github.com/z7zmey/php-parser/pkg/conf" 22 | "github.com/z7zmey/php-parser/pkg/errors" 23 | "github.com/z7zmey/php-parser/pkg/parser" 24 | "github.com/z7zmey/php-parser/pkg/version" 25 | "github.com/z7zmey/php-parser/pkg/visitor/dumper" 26 | "github.com/z7zmey/php-parser/pkg/visitor/nsresolver" 27 | "github.com/z7zmey/php-parser/pkg/visitor/printer" 28 | "github.com/z7zmey/php-parser/pkg/visitor/traverser" 29 | ) 30 | 31 | var wg sync.WaitGroup 32 | var phpVersion *version.Version 33 | var profiler string 34 | var dump *bool 35 | var showResolvedNs *bool 36 | var printBack *bool 37 | var printPath *bool 38 | var printErrors *bool 39 | var printExecTime *bool 40 | 41 | type file struct { 42 | path string 43 | content []byte 44 | } 45 | 46 | type result struct { 47 | path string 48 | rootNode ast.Vertex 49 | errors []*errors.Error 50 | } 51 | 52 | func main() { 53 | start := time.Now() 54 | var phpVer string 55 | 56 | printExecTime = flag.Bool("time", false, "print execution time") 57 | showResolvedNs = flag.Bool("r", false, "resolve names") 58 | printBack = flag.Bool("pb", false, "print AST back into the parsed file") 59 | printPath = flag.Bool("p", false, "print filepath") 60 | printErrors = flag.Bool("e", false, "print errors") 61 | dump = flag.Bool("d", false, "dump") 62 | flag.StringVar(&profiler, "prof", "", "start profiler: [cpu, mem, trace]") 63 | flag.StringVar(&phpVer, "phpver", "7.4", "php version") 64 | 65 | flag.Parse() 66 | 67 | var err error 68 | phpVersion, err = version.New(phpVer) 69 | if err != nil { 70 | fmt.Println("Error: " + err.Error()) 71 | os.Exit(1) 72 | } 73 | 74 | if len(flag.Args()) == 0 { 75 | flag.Usage() 76 | return 77 | } 78 | 79 | switch profiler { 80 | case "cpu": 81 | defer profile.Start(profile.ProfilePath("."), profile.NoShutdownHook).Stop() 82 | case "mem": 83 | defer profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop() 84 | case "trace": 85 | defer profile.Start(profile.TraceProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop() 86 | } 87 | 88 | numCpu := runtime.GOMAXPROCS(0) 89 | 90 | fileCh := make(chan *file, numCpu) 91 | resultCh := make(chan result, numCpu) 92 | 93 | // run 4 concurrent parserWorkers 94 | for i := 0; i < numCpu; i++ { 95 | go parserWorker(fileCh, resultCh) 96 | } 97 | 98 | // run printer goroutine 99 | go printerWorker(resultCh) 100 | 101 | // process files 102 | processPath(flag.Args(), fileCh) 103 | 104 | // wait the all files done 105 | wg.Wait() 106 | close(fileCh) 107 | close(resultCh) 108 | 109 | elapsed := time.Since(start) 110 | if *printExecTime { 111 | log.Printf("took: %s", elapsed) 112 | } 113 | } 114 | 115 | func processPath(pathList []string, fileCh chan<- *file) { 116 | for _, path := range pathList { 117 | real, err := realpath.Realpath(path) 118 | checkErr(err) 119 | 120 | err = filepath.Walk(real, func(path string, f os.FileInfo, err error) error { 121 | if !f.IsDir() && filepath.Ext(path) == ".php" { 122 | wg.Add(1) 123 | content, err := ioutil.ReadFile(path) 124 | checkErr(err) 125 | fileCh <- &file{path, content} 126 | } 127 | return nil 128 | }) 129 | checkErr(err) 130 | } 131 | } 132 | 133 | func parserWorker(fileCh <-chan *file, r chan<- result) { 134 | for { 135 | f, ok := <-fileCh 136 | if !ok { 137 | return 138 | } 139 | 140 | var parserErrors []*errors.Error 141 | rootNode, err := parser.Parse(f.content, conf.Config{ 142 | Version: phpVersion, 143 | ErrorHandlerFunc: func(e *errors.Error) { 144 | parserErrors = append(parserErrors, e) 145 | }, 146 | }) 147 | if err != nil { 148 | fmt.Println("Error:" + err.Error()) 149 | os.Exit(1) 150 | } 151 | 152 | r <- result{path: f.path, rootNode: rootNode, errors: parserErrors} 153 | } 154 | } 155 | 156 | func printerWorker(r <-chan result) { 157 | var counter int 158 | 159 | for { 160 | res, ok := <-r 161 | if !ok { 162 | return 163 | } 164 | 165 | counter++ 166 | 167 | if *printPath { 168 | _, _ = io.WriteString(os.Stderr, "==> ["+strconv.Itoa(counter)+"] "+res.path+"\n") 169 | } 170 | 171 | if *printErrors { 172 | for _, e := range res.errors { 173 | _, _ = io.WriteString(os.Stderr, "==> "+e.String()+"\n") 174 | } 175 | } 176 | 177 | if *printBack { 178 | o := bytes.NewBuffer([]byte{}) 179 | p := printer.NewPrinter(o) 180 | res.rootNode.Accept(p) 181 | 182 | err := ioutil.WriteFile(res.path, o.Bytes(), 0644) 183 | checkErr(err) 184 | } 185 | 186 | if *showResolvedNs { 187 | v := nsresolver.NewNamespaceResolver() 188 | traverser.NewTraverser(v).Traverse(res.rootNode) 189 | for _, n := range v.ResolvedNames { 190 | _, _ = io.WriteString(os.Stderr, "===> "+n+"\n") 191 | } 192 | } 193 | 194 | if *dump == true { 195 | dumper.NewDumper(os.Stdout).WithPositions().WithTokens().Dump(res.rootNode) 196 | } 197 | 198 | wg.Done() 199 | } 200 | } 201 | 202 | func checkErr(err error) { 203 | if err != nil { 204 | log.Fatal(err) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/z7zmey/php-parser 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/google/go-cmp v0.4.0 // indirect 7 | github.com/pkg/errors v0.9.1 // indirect 8 | github.com/pkg/profile v1.4.0 9 | github.com/yookoala/realpath v1.0.0 10 | gotest.tools v2.2.0+incompatible 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 2 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI= 6 | github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= 7 | github.com/yookoala/realpath v1.0.0 h1:7OA9pj4FZd+oZDsyvXWQvjn5oBdcHRTV44PpdMSuImQ= 8 | github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= 9 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 10 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 11 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 12 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 13 | -------------------------------------------------------------------------------- /internal/php5/node.go: -------------------------------------------------------------------------------- 1 | package php5 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/ast" 5 | "github.com/z7zmey/php-parser/pkg/position" 6 | "github.com/z7zmey/php-parser/pkg/token" 7 | ) 8 | 9 | type ParserBrackets struct { 10 | Position *position.Position 11 | OpenBracketTkn *token.Token 12 | Child ast.Vertex 13 | CloseBracketTkn *token.Token 14 | } 15 | 16 | func (n *ParserBrackets) Accept(v ast.Visitor) { 17 | // do nothing 18 | } 19 | 20 | func (n *ParserBrackets) GetPosition() *position.Position { 21 | return n.Position 22 | } 23 | 24 | type ParserSeparatedList struct { 25 | Position *position.Position 26 | Items []ast.Vertex 27 | SeparatorTkns []*token.Token 28 | } 29 | 30 | func (n *ParserSeparatedList) Accept(v ast.Visitor) { 31 | // do nothing 32 | } 33 | 34 | func (n *ParserSeparatedList) GetPosition() *position.Position { 35 | return n.Position 36 | } 37 | 38 | // TraitAdaptationList node 39 | type TraitAdaptationList struct { 40 | Position *position.Position 41 | OpenCurlyBracketTkn *token.Token 42 | Adaptations []ast.Vertex 43 | CloseCurlyBracketTkn *token.Token 44 | } 45 | 46 | func (n *TraitAdaptationList) Accept(v ast.Visitor) { 47 | // do nothing 48 | } 49 | 50 | func (n *TraitAdaptationList) GetPosition() *position.Position { 51 | return n.Position 52 | } 53 | 54 | // ArgumentList node 55 | type ArgumentList struct { 56 | Position *position.Position 57 | OpenParenthesisTkn *token.Token 58 | Arguments []ast.Vertex 59 | SeparatorTkns []*token.Token 60 | CloseParenthesisTkn *token.Token 61 | } 62 | 63 | func (n *ArgumentList) Accept(v ast.Visitor) { 64 | // do nothing 65 | } 66 | 67 | func (n *ArgumentList) GetPosition() *position.Position { 68 | return n.Position 69 | } 70 | 71 | // TraitMethodRef node 72 | type TraitMethodRef struct { 73 | Position *position.Position 74 | Trait ast.Vertex 75 | DoubleColonTkn *token.Token 76 | Method ast.Vertex 77 | } 78 | 79 | func (n *TraitMethodRef) Accept(v ast.Visitor) { 80 | // do nothing 81 | } 82 | 83 | func (n *TraitMethodRef) GetPosition() *position.Position { 84 | return n.Position 85 | } 86 | -------------------------------------------------------------------------------- /internal/php5/parser.go: -------------------------------------------------------------------------------- 1 | package php5 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/internal/position" 5 | "github.com/z7zmey/php-parser/internal/scanner" 6 | "github.com/z7zmey/php-parser/pkg/ast" 7 | "github.com/z7zmey/php-parser/pkg/conf" 8 | "github.com/z7zmey/php-parser/pkg/errors" 9 | "github.com/z7zmey/php-parser/pkg/token" 10 | ) 11 | 12 | // Parser structure 13 | type Parser struct { 14 | Lexer *scanner.Lexer 15 | currentToken *token.Token 16 | rootNode ast.Vertex 17 | errHandlerFunc func(*errors.Error) 18 | builder *position.Builder 19 | } 20 | 21 | // NewParser creates and returns new Parser 22 | func NewParser(lexer *scanner.Lexer, config conf.Config) *Parser { 23 | return &Parser{ 24 | Lexer: lexer, 25 | errHandlerFunc: config.ErrorHandlerFunc, 26 | builder: position.NewBuilder(), 27 | } 28 | } 29 | 30 | // Lex proxy to scanner Lex 31 | func (p *Parser) Lex(lval *yySymType) int { 32 | t := p.Lexer.Lex() 33 | 34 | p.currentToken = t 35 | lval.token = t 36 | 37 | return int(t.ID) 38 | } 39 | 40 | func (p *Parser) Error(msg string) { 41 | if p.errHandlerFunc == nil { 42 | return 43 | } 44 | 45 | p.errHandlerFunc(errors.NewError(msg, p.currentToken.Position)) 46 | } 47 | 48 | // Parse the php7 Parser entrypoint 49 | func (p *Parser) Parse() int { 50 | p.rootNode = nil 51 | return yyParse(p) 52 | } 53 | 54 | // GetRootNode returns root node 55 | func (p *Parser) GetRootNode() ast.Vertex { 56 | return p.rootNode 57 | } 58 | 59 | // helpers 60 | 61 | func lastNode(nn []ast.Vertex) ast.Vertex { 62 | if len(nn) == 0 { 63 | return nil 64 | } 65 | return nn[len(nn)-1] 66 | } 67 | -------------------------------------------------------------------------------- /internal/php5/php5_bench_test.go: -------------------------------------------------------------------------------- 1 | package php5_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | "github.com/z7zmey/php-parser/internal/php5" 8 | "github.com/z7zmey/php-parser/internal/scanner" 9 | "github.com/z7zmey/php-parser/pkg/conf" 10 | "github.com/z7zmey/php-parser/pkg/version" 11 | ) 12 | 13 | func BenchmarkPhp5(b *testing.B) { 14 | src, err := ioutil.ReadFile("test.php") 15 | if err != nil { 16 | b.Fatal("can not read test.php: " + err.Error()) 17 | } 18 | 19 | for n := 0; n < b.N; n++ { 20 | config := conf.Config{ 21 | Version: &version.Version{ 22 | Major: 5, 23 | Minor: 6, 24 | }, 25 | } 26 | lexer := scanner.NewLexer(src, config) 27 | php5parser := php5.NewParser(lexer, config) 28 | php5parser.Parse() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/php5/test.php: -------------------------------------------------------------------------------- 1 | bar($a, ...$b); 5 | foo::bar($a, ...$b); 6 | $foo::bar($a, ...$b); 7 | new foo($a, ...$b); 8 | 9 | function foo(bar $bar=null, baz &...$baz) {} 10 | class foo {public function foo(bar $bar=null, baz &...$baz) {}} 11 | function(bar $bar=null, baz &...$baz) {}; 12 | static function(bar $bar=null, baz &...$baz) {}; 13 | 14 | 1234567890123456789; 15 | 12345678901234567890; 16 | 0.; 17 | 0b0111111111111111111111111111111111111111111111111111111111111111; 18 | 0b1111111111111111111111111111111111111111111111111111111111111111; 19 | 0x007111111111111111; 20 | 0x8111111111111111; 21 | __CLASS__; 22 | __DIR__; 23 | __FILE__; 24 | __FUNCTION__; 25 | __LINE__; 26 | __NAMESPACE__; 27 | __METHOD__; 28 | __TRAIT__; 29 | 30 | "test $var"; 31 | "test $var[1]"; 32 | "test $var[1234567890123456789012345678901234567890]"; 33 | "test $var[bar]"; 34 | "test $var[$bar]"; 35 | "$foo $bar"; 36 | "test $foo->bar()"; 37 | "test ${foo}"; 38 | "test ${foo[0]}"; 39 | "test {$foo->bar()}"; 40 | 41 | if ($a) : 42 | endif; 43 | if ($a) : 44 | elseif ($b): 45 | endif; 46 | if ($a) : 47 | else: 48 | endif; 49 | if ($a) : 50 | elseif ($b): 51 | elseif ($c): 52 | else: 53 | endif; 54 | 55 | while (1) { break; } 56 | while (1) { break 2; } 57 | while (1) : break(3); endwhile; 58 | class foo{ const FOO = 1, BAR = 2; } 59 | class foo{ function bar() {} } 60 | class foo{ public static function &bar() {} } 61 | class foo{ final private function bar() {} protected function baz() {} } 62 | abstract class foo{ abstract public function bar(); } 63 | final class foo extends bar { } 64 | final class foo implements bar { } 65 | final class foo implements bar, baz { } 66 | 67 | const FOO = 1, BAR = 2; 68 | while (1) { continue; } 69 | while (1) { continue 2; } 70 | while (1) { continue(3); } 71 | declare(ticks=1); 72 | declare(ticks=1, strict_types=1) {} 73 | declare(ticks=1): enddeclare; 74 | do {} while(1); 75 | echo $a, 1; 76 | echo($a); 77 | for($i = 0; $i < 10; $i++, $i++) {} 78 | for(; $i < 10; $i++) : endfor; 79 | foreach ($a as $v) {} 80 | foreach ([] as $v) {} 81 | foreach ($a as $v) : endforeach; 82 | foreach ($a as $k => $v) {} 83 | foreach ([] as $k => $v) {} 84 | foreach ($a as $k => &$v) {} 85 | foreach ($a as $k => list($v)) {} 86 | function foo() {} 87 | 88 | function foo() { 89 | function bar() {} 90 | class Baz {} 91 | return $a; 92 | } 93 | 94 | function foo(array $a, callable $b) {return;} 95 | function &foo() {return 1;} 96 | function &foo() {} 97 | global $a, $b, $$c, ${foo()}; 98 | a: 99 | goto a; 100 | if ($a) {} 101 | if ($a) {} elseif ($b) {} 102 | if ($a) {} else {} 103 | if ($a) {} elseif ($b) {} elseif ($c) {} else {} 104 | if ($a) {} elseif ($b) {} else if ($c) {} else {} 105 | ?>
1, &$b,); 169 | array(3 =>&$b); 170 | array(&$b, 1=>1, 1, 3 =>&$b); 171 | ~$a; 172 | !$a; 173 | 174 | Foo::Bar; 175 | clone($a); 176 | clone $a; 177 | function(){}; 178 | function($a, $b) use ($c, &$d) {}; 179 | function($a, $b) use (&$c, $d) {}; 180 | function() {}; 181 | foo; 182 | namespace\foo; 183 | \foo; 184 | 185 | empty($a); 186 | empty(Foo); 187 | @$a; 188 | eval($a); 189 | exit; 190 | exit($a); 191 | die(); 192 | die($a); 193 | foo(); 194 | namespace\foo(&$a); 195 | \foo([]); 196 | $foo(yield $a); 197 | 198 | $a--; 199 | $a++; 200 | --$a; 201 | ++$a; 202 | 203 | include $a; 204 | include_once $a; 205 | require $a; 206 | require_once $a; 207 | 208 | $a instanceof Foo; 209 | $a instanceof namespace\Foo; 210 | $a instanceof \Foo; 211 | 212 | isset($a, $b); 213 | isset(Foo); 214 | list() = $b; 215 | list($a, $b) = $b; 216 | list($a[]) = $b; 217 | list(list($a)) = $b; 218 | 219 | $a->foo(); 220 | new Foo; 221 | new namespace\Foo(); 222 | new \Foo(); 223 | print($a); 224 | $a->foo; 225 | $a->foo[1]; 226 | $a->foo->bar->baz()->quux[0]; 227 | $a->foo()[1][1]; 228 | `cmd $a`; 229 | `cmd`; 230 | ``; 231 | []; 232 | [1]; 233 | [1=>1, &$b,]; 234 | 235 | Foo::bar(); 236 | namespace\Foo::bar(); 237 | \Foo::bar(); 238 | Foo::$bar(); 239 | $foo::$bar(); 240 | Foo::$bar; 241 | namespace\Foo::$bar; 242 | \Foo::$bar; 243 | $a ? $b : $c; 244 | $a ? : $c; 245 | $a ? $b ? $c : $d : $e; 246 | $a ? $b : $c ? $d : $e; 247 | -$a; 248 | +$a; 249 | $$a; 250 | $$$a; 251 | yield; 252 | yield $a; 253 | yield $a => $b; 254 | yield Foo::class; 255 | yield $a => Foo::class; 256 | 257 | (array)$a; 258 | (boolean)$a; 259 | (bool)$a; 260 | (double)$a; 261 | (float)$a; 262 | (integer)$a; 263 | (int)$a; 264 | (object)$a; 265 | (string)$a; 266 | (unset)$a; 267 | 268 | $a & $b; 269 | $a | $b; 270 | $a ^ $b; 271 | $a && $b; 272 | $a || $b; 273 | $a . $b; 274 | $a / $b; 275 | $a == $b; 276 | $a >= $b; 277 | $a > $b; 278 | $a === $b; 279 | $a and $b; 280 | $a or $b; 281 | $a xor $b; 282 | $a - $b; 283 | $a % $b; 284 | $a * $b; 285 | $a != $b; 286 | $a !== $b; 287 | $a + $b; 288 | $a ** $b; 289 | $a << $b; 290 | $a >> $b; 291 | $a <= $b; 292 | $a < $b; 293 | 294 | $a =& $b; 295 | $a =& new Foo; 296 | $a =& new Foo($b); 297 | $a = $b; 298 | $a &= $b; 299 | $a |= $b; 300 | $a ^= $b; 301 | $a .= $b; 302 | $a /= $b; 303 | $a -= $b; 304 | $a %= $b; 305 | $a *= $b; 306 | $a += $b; 307 | $a **= $b; 308 | $a <<= $b; 309 | $a >>= $b; 310 | 311 | 312 | (new \Foo()); 313 | (new \Foo())->bar()->baz; 314 | (new \Foo())[0][0]; 315 | (new \Foo())[0]->bar(); 316 | 317 | array([0])[0][0]; 318 | "foo"[0]; 319 | foo[0]; 320 | static::foo; 321 | 322 | new $foo; 323 | new $foo::$bar; 324 | new $a->b[0]; 325 | new $a->b{$b ?: null}->$c->d[0];static $a = [1][0]; 326 | 327 | static $a = !1; 328 | static $a = ~1; 329 | static $a = +1; 330 | static $a = -1; 331 | static $a = (1); 332 | static $a = 1 ?: 2; 333 | static $a = 1 ? 2 : 3; 334 | static $a = 1 & 2; 335 | static $a = 1 | 2; 336 | static $a = 1 ^ 2; 337 | static $a = 1 && 2; 338 | static $a = 1 || 2; 339 | static $a = 1 . 2; 340 | static $a = 1 / 2; 341 | static $a = 1 == 2; 342 | static $a = 1 >= 2; 343 | static $a = 1 > 2; 344 | static $a = 1 === 2; 345 | static $a = 1 and 2; 346 | static $a = 1 or 2; 347 | static $a = 1 xor 2; 348 | static $a = 1 - 2; 349 | static $a = 1 % 2; 350 | static $a = 1 * 2; 351 | static $a = 1 != 2; 352 | static $a = 1 !== 2; 353 | static $a = 1 + 2; 354 | static $a = 1 ** 2; 355 | static $a = 1 << 2; 356 | static $a = 1 >> 2; 357 | static $a = 1 <= 2; 358 | static $a = 1 < 2; 359 | static $a = Foo::bar; 360 | static $a = Foo::class; 361 | static $a = __CLASS__; 362 | static $a = Foo; 363 | static $a = namespace\Foo; 364 | static $a = \Foo; 365 | static $a = array(); 366 | static $a = array(1 => 1, 2); 367 | static $a = [1, 2 => 2][0]; 368 | 369 | if (yield 1) {} 370 | Foo::$$bar; 371 | 372 | $foo(); 373 | $foo()[0][0]; 374 | $a{$b}; 375 | ${$a}; 376 | $foo::{$bar}(); 377 | $foo::bar; 378 | 379 | __halt_compiler(); 380 | 381 | parsing process must be terminated -------------------------------------------------------------------------------- /internal/php7/node.go: -------------------------------------------------------------------------------- 1 | package php7 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/ast" 5 | "github.com/z7zmey/php-parser/pkg/position" 6 | "github.com/z7zmey/php-parser/pkg/token" 7 | ) 8 | 9 | type ParserBrackets struct { 10 | Position *position.Position 11 | OpenBracketTkn *token.Token 12 | Child ast.Vertex 13 | CloseBracketTkn *token.Token 14 | } 15 | 16 | func (n *ParserBrackets) Accept(v ast.Visitor) { 17 | // do nothing 18 | } 19 | 20 | func (n *ParserBrackets) GetPosition() *position.Position { 21 | return n.Position 22 | } 23 | 24 | type ParserSeparatedList struct { 25 | Position *position.Position 26 | Items []ast.Vertex 27 | SeparatorTkns []*token.Token 28 | } 29 | 30 | func (n *ParserSeparatedList) Accept(v ast.Visitor) { 31 | // do nothing 32 | } 33 | 34 | func (n *ParserSeparatedList) GetPosition() *position.Position { 35 | return n.Position 36 | } 37 | 38 | // TraitAdaptationList node 39 | type TraitAdaptationList struct { 40 | Position *position.Position 41 | OpenCurlyBracketTkn *token.Token 42 | Adaptations []ast.Vertex 43 | CloseCurlyBracketTkn *token.Token 44 | } 45 | 46 | func (n *TraitAdaptationList) Accept(v ast.Visitor) { 47 | // do nothing 48 | } 49 | 50 | func (n *TraitAdaptationList) GetPosition() *position.Position { 51 | return n.Position 52 | } 53 | 54 | // ArgumentList node 55 | type ArgumentList struct { 56 | Position *position.Position 57 | OpenParenthesisTkn *token.Token 58 | Arguments []ast.Vertex 59 | SeparatorTkns []*token.Token 60 | CloseParenthesisTkn *token.Token 61 | } 62 | 63 | func (n *ArgumentList) Accept(v ast.Visitor) { 64 | // do nothing 65 | } 66 | 67 | func (n *ArgumentList) GetPosition() *position.Position { 68 | return n.Position 69 | } 70 | 71 | type ReturnType struct { 72 | Position *position.Position 73 | ColonTkn *token.Token 74 | Type ast.Vertex 75 | } 76 | 77 | func (n *ReturnType) Accept(v ast.Visitor) { 78 | // do nothing 79 | } 80 | 81 | func (n *ReturnType) GetPosition() *position.Position { 82 | return n.Position 83 | } 84 | 85 | // TraitMethodRef node 86 | type TraitMethodRef struct { 87 | Position *position.Position 88 | Trait ast.Vertex 89 | DoubleColonTkn *token.Token 90 | Method ast.Vertex 91 | } 92 | 93 | func (n *TraitMethodRef) Accept(v ast.Visitor) { 94 | // do nothing 95 | } 96 | 97 | func (n *TraitMethodRef) GetPosition() *position.Position { 98 | return n.Position 99 | } 100 | -------------------------------------------------------------------------------- /internal/php7/parser.go: -------------------------------------------------------------------------------- 1 | package php7 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/internal/position" 5 | "github.com/z7zmey/php-parser/internal/scanner" 6 | "github.com/z7zmey/php-parser/pkg/ast" 7 | "github.com/z7zmey/php-parser/pkg/conf" 8 | "github.com/z7zmey/php-parser/pkg/errors" 9 | "github.com/z7zmey/php-parser/pkg/token" 10 | ) 11 | 12 | // Parser structure 13 | type Parser struct { 14 | Lexer *scanner.Lexer 15 | currentToken *token.Token 16 | rootNode ast.Vertex 17 | errHandlerFunc func(*errors.Error) 18 | builder *position.Builder 19 | } 20 | 21 | // NewParser creates and returns new Parser 22 | func NewParser(lexer *scanner.Lexer, config conf.Config) *Parser { 23 | return &Parser{ 24 | Lexer: lexer, 25 | errHandlerFunc: config.ErrorHandlerFunc, 26 | builder: position.NewBuilder(), 27 | } 28 | } 29 | 30 | func (p *Parser) Lex(lval *yySymType) int { 31 | t := p.Lexer.Lex() 32 | 33 | p.currentToken = t 34 | lval.token = t 35 | 36 | return int(t.ID) 37 | } 38 | 39 | func (p *Parser) Error(msg string) { 40 | if p.errHandlerFunc == nil { 41 | return 42 | } 43 | 44 | p.errHandlerFunc(errors.NewError(msg, p.currentToken.Position)) 45 | } 46 | 47 | // Parse the php7 Parser entrypoint 48 | func (p *Parser) Parse() int { 49 | p.rootNode = nil 50 | 51 | return yyParse(p) 52 | } 53 | 54 | // GetRootNode returns root node 55 | func (p *Parser) GetRootNode() ast.Vertex { 56 | return p.rootNode 57 | } 58 | 59 | // helpers 60 | 61 | func lastNode(nn []ast.Vertex) ast.Vertex { 62 | if len(nn) == 0 { 63 | return nil 64 | } 65 | return nn[len(nn)-1] 66 | } 67 | -------------------------------------------------------------------------------- /internal/php7/php7_bench_test.go: -------------------------------------------------------------------------------- 1 | package php7_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | "github.com/z7zmey/php-parser/internal/php7" 8 | "github.com/z7zmey/php-parser/internal/scanner" 9 | "github.com/z7zmey/php-parser/pkg/conf" 10 | "github.com/z7zmey/php-parser/pkg/version" 11 | ) 12 | 13 | func BenchmarkPhp7(b *testing.B) { 14 | src, err := ioutil.ReadFile("test.php") 15 | 16 | if err != nil { 17 | b.Fatal("can not read test.php: " + err.Error()) 18 | } 19 | 20 | for n := 0; n < b.N; n++ { 21 | config := conf.Config{ 22 | Version: &version.Version{ 23 | Major: 7, 24 | Minor: 4, 25 | }, 26 | } 27 | lexer := scanner.NewLexer(src, config) 28 | php7parser := php7.NewParser(lexer, config) 29 | php7parser.Parse() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/php7/test.php: -------------------------------------------------------------------------------- 1 | bar($a, ...$b); 5 | foo::bar($a, ...$b); 6 | $foo::bar($a, ...$b); 7 | new foo($a, ...$b); 8 | /** anonymous class */ 9 | new class ($a, ...$b) {}; 10 | new class {}; 11 | new $foo; 12 | new $foo[1]; 13 | new $foo{$bar}; 14 | new $foo->bar; 15 | new $foo::$bar; 16 | new static::$bar; 17 | 18 | function foo(?bar $bar=null, baz &...$baz) {} 19 | class foo {public function foo(?bar $bar=null, baz &...$baz) {}} 20 | function(?bar $bar=null, baz &...$baz) {}; 21 | static function(?bar $bar=null, baz &...$baz) {}; 22 | 23 | 1234567890123456789; 24 | 12345678901234567890; 25 | 0.; 26 | 0b0111111111111111111111111111111111111111111111111111111111111111; 27 | 0b1111111111111111111111111111111111111111111111111111111111111111; 28 | 0x007111111111111111; 29 | 0x8111111111111111; 30 | __CLASS__; 31 | __DIR__; 32 | __FILE__; 33 | __FUNCTION__; 34 | __LINE__; 35 | __NAMESPACE__; 36 | __METHOD__; 37 | __TRAIT__; 38 | 39 | "test $var"; 40 | "test $var[1]"; 41 | "test $var[-1]"; 42 | "test $var[1234567890123456789012345678901234567890]"; 43 | "test $var[-1234567890123456789012345678901234567890]"; 44 | "test $var[bar]"; 45 | "test $var[$bar]"; 46 | "$foo $bar"; 47 | "test $foo->bar()"; 48 | "test ${foo}"; 49 | "test ${foo[0]}"; 50 | "test ${$foo}"; 51 | "test {$foo->bar()}"; 52 | 53 | if ($a) : 54 | endif; 55 | if ($a) : 56 | elseif ($b): 57 | endif; 58 | if ($a) : 59 | else: 60 | endif; 61 | if ($a) : 62 | elseif ($b): 63 | elseif ($c): 64 | else: 65 | endif; 66 | 67 | while (1) { break; } 68 | while (1) { break 2; } 69 | while (1) : break(3); endwhile; 70 | class foo{ public const FOO = 1, BAR = 2; } 71 | class foo{ const FOO = 1, BAR = 2; } 72 | class foo{ function bar() {} } 73 | class foo{ public static function &bar() {} } 74 | class foo{ public static function &bar(): void {} } 75 | abstract class foo{ } 76 | final class foo extends bar { } 77 | final class foo implements bar { } 78 | final class foo implements bar, baz { } 79 | new class() extends foo implements bar, baz { }; 80 | 81 | const FOO = 1, BAR = 2; 82 | while (1) { continue; } 83 | while (1) { continue 2; } 84 | while (1) { continue(3); } 85 | declare(ticks=1); 86 | declare(ticks=1) {} 87 | declare(ticks=1): enddeclare; 88 | do {} while(1); 89 | echo $a, 1; 90 | echo($a); 91 | for($i = 0; $i < 10; $i++, $i++) {} 92 | for(; $i < 10; $i++, $i++) : endfor; 93 | foreach ($a as $v) {} 94 | foreach ($a as $v) : endforeach; 95 | foreach ($a as $k => $v) {} 96 | foreach ($a as $k => &$v) {} 97 | foreach ($a as $k => list($v)) {} 98 | foreach ($a as $k => [$v]) {} 99 | function foo() {} 100 | function foo() {return;} 101 | function &foo() {return 1;} 102 | function &foo(): void {} 103 | global $a, $b; 104 | a: 105 | goto a; 106 | if ($a) {} 107 | if ($a) {} elseif ($b) {} 108 | if ($a) {} else {} 109 | if ($a) {} elseif ($b) {} elseif ($c) {} else {} 110 | if ($a) {} elseif ($b) {} else if ($c) {} else {} 111 | ?>
1, &$b,); 182 | ~$a; 183 | !$a; 184 | 185 | Foo::Bar; 186 | $foo::Bar; 187 | clone($a); 188 | clone $a; 189 | function(){}; 190 | function($a, $b) use ($c, &$d) {}; 191 | function(): void {}; 192 | foo; 193 | namespace\foo; 194 | \foo; 195 | 196 | empty($a); 197 | @$a; 198 | eval($a); 199 | exit; 200 | exit($a); 201 | die; 202 | die($a); 203 | foo(); 204 | namespace\foo(); 205 | \foo(); 206 | $foo(); 207 | 208 | $a--; 209 | $a++; 210 | --$a; 211 | ++$a; 212 | 213 | include $a; 214 | include_once $a; 215 | require $a; 216 | require_once $a; 217 | 218 | $a instanceof Foo; 219 | $a instanceof namespace\Foo; 220 | $a instanceof \Foo; 221 | 222 | isset($a, $b); 223 | list($a) = $b; 224 | list($a[]) = $b; 225 | list(list($a)) = $b; 226 | 227 | $a->foo(); 228 | new Foo(); 229 | new namespace\Foo(); 230 | new \Foo(); 231 | new class ($a, ...$b) {}; 232 | print($a); 233 | $a->foo; 234 | `cmd $a`; 235 | `cmd`; 236 | ``; 237 | []; 238 | [1]; 239 | [1=>1, &$b,]; 240 | 241 | [$a] = $b; 242 | [$a[]] = $b; 243 | [list($a)] = $b; 244 | Foo::bar(); 245 | namespace\Foo::bar(); 246 | \Foo::bar(); 247 | Foo::$bar; 248 | $foo::$bar; 249 | namespace\Foo::$bar; 250 | \Foo::$bar; 251 | $a ? $b : $c; 252 | $a ? : $c; 253 | $a ? $b ? $c : $d : $e; 254 | $a ? $b : $c ? $d : $e; 255 | -$a; 256 | +$a; 257 | $$a; 258 | yield; 259 | yield $a; 260 | yield $a => $b; 261 | yield from $a; 262 | 263 | (array)$a; 264 | (boolean)$a; 265 | (bool)$a; 266 | (double)$a; 267 | (float)$a; 268 | (integer)$a; 269 | (int)$a; 270 | (object)$a; 271 | (string)$a; 272 | (unset)$a; 273 | 274 | $a & $b; 275 | $a | $b; 276 | $a ^ $b; 277 | $a && $b; 278 | $a || $b; 279 | $a ?? $b; 280 | $a . $b; 281 | $a / $b; 282 | $a == $b; 283 | $a >= $b; 284 | $a > $b; 285 | $a === $b; 286 | $a and $b; 287 | $a or $b; 288 | $a xor $b; 289 | $a - $b; 290 | $a % $b; 291 | $a * $b; 292 | $a != $b; 293 | $a !== $b; 294 | $a + $b; 295 | $a ** $b; 296 | $a << $b; 297 | $a >> $b; 298 | $a <= $b; 299 | $a < $b; 300 | $a <=> $b; 301 | 302 | $a =& $b; 303 | $a = $b; 304 | $a &= $b; 305 | $a |= $b; 306 | $a ^= $b; 307 | $a .= $b; 308 | $a /= $b; 309 | $a -= $b; 310 | $a %= $b; 311 | $a *= $b; 312 | $a += $b; 313 | $a **= $b; 314 | $a <<= $b; 315 | $a >>= $b; 316 | 317 | class foo {public function class() {} } 318 | \foo\bar(); 319 | 320 | function foo(&$a, ...$b) { 321 | 322 | function bar() {} 323 | class Baz {} 324 | trait Quux{} 325 | interface Quuux {} 326 | } 327 | 328 | function foo(&$a = 1, ...$b = 1, $c = 1) {} 329 | function foo(array $a, callable $b) {} 330 | abstract final class foo { abstract protected static function bar(); final private function baz() {} } 331 | 332 | (new Foo)->bar; 333 | (new Foo)(); 334 | [$foo][0](); 335 | foo[1](); 336 | "foo"(); 337 | [1]{$foo}(); 338 | ${foo()}; 339 | 340 | Foo::$bar(); 341 | Foo::{$bar[0]}(); 342 | 343 | $foo->$bar; 344 | $foo->{$bar[0]}; 345 | 346 | [1=>&$a, 2=>list($b)]; 347 | 348 | __halt_compiler(); 349 | 350 | parsing process must be terminated -------------------------------------------------------------------------------- /internal/position/position.go: -------------------------------------------------------------------------------- 1 | package position 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/ast" 5 | "github.com/z7zmey/php-parser/pkg/position" 6 | "github.com/z7zmey/php-parser/pkg/token" 7 | ) 8 | 9 | type startPos struct { 10 | startLine int 11 | startPos int 12 | } 13 | 14 | type endPos struct { 15 | endLine int 16 | endPos int 17 | } 18 | 19 | type Builder struct { 20 | pool *position.Pool 21 | } 22 | 23 | func NewBuilder() *Builder { 24 | return &Builder{ 25 | pool: position.NewPool(position.DefaultBlockSize), 26 | } 27 | } 28 | 29 | func getListStartPos(l []ast.Vertex) startPos { 30 | if l == nil { 31 | return startPos{-1, -1} 32 | } 33 | 34 | if len(l) == 0 { 35 | return startPos{-1, -1} 36 | } 37 | 38 | return getNodeStartPos(l[0]) 39 | } 40 | 41 | func getNodeStartPos(n ast.Vertex) startPos { 42 | sl := -1 43 | sp := -1 44 | 45 | if n == nil { 46 | return startPos{-1, -1} 47 | } 48 | 49 | p := n.GetPosition() 50 | if p != nil { 51 | sl = p.StartLine 52 | sp = p.StartPos 53 | } 54 | 55 | return startPos{sl, sp} 56 | } 57 | 58 | func getListEndPos(l []ast.Vertex) endPos { 59 | if l == nil { 60 | return endPos{-1, -1} 61 | } 62 | 63 | if len(l) == 0 { 64 | return endPos{-1, -1} 65 | } 66 | 67 | return getNodeEndPos(l[len(l)-1]) 68 | } 69 | 70 | func getNodeEndPos(n ast.Vertex) endPos { 71 | el := -1 72 | ep := -1 73 | 74 | if n == nil { 75 | return endPos{-1, -1} 76 | } 77 | 78 | p := n.GetPosition() 79 | if p != nil { 80 | el = p.EndLine 81 | ep = p.EndPos 82 | } 83 | 84 | return endPos{el, ep} 85 | } 86 | 87 | // NewNodeListPosition returns new Position 88 | func (b *Builder) NewNodeListPosition(list []ast.Vertex) *position.Position { 89 | pos := b.pool.Get() 90 | 91 | pos.StartLine = getListStartPos(list).startLine 92 | pos.EndLine = getListEndPos(list).endLine 93 | pos.StartPos = getListStartPos(list).startPos 94 | pos.EndPos = getListEndPos(list).endPos 95 | 96 | return pos 97 | } 98 | 99 | // NewNodePosition returns new Position 100 | func (b *Builder) NewNodePosition(n ast.Vertex) *position.Position { 101 | pos := b.pool.Get() 102 | 103 | pos.StartLine = getNodeStartPos(n).startLine 104 | pos.EndLine = getNodeEndPos(n).endLine 105 | pos.StartPos = getNodeStartPos(n).startPos 106 | pos.EndPos = getNodeEndPos(n).endPos 107 | 108 | return pos 109 | } 110 | 111 | // NewTokenPosition returns new Position 112 | func (b *Builder) NewTokenPosition(t *token.Token) *position.Position { 113 | pos := b.pool.Get() 114 | 115 | pos.StartLine = t.Position.StartLine 116 | pos.EndLine = t.Position.EndLine 117 | pos.StartPos = t.Position.StartPos 118 | pos.EndPos = t.Position.EndPos 119 | 120 | return pos 121 | } 122 | 123 | // NewTokensPosition returns new Position 124 | func (b *Builder) NewTokensPosition(startToken *token.Token, endToken *token.Token) *position.Position { 125 | pos := b.pool.Get() 126 | 127 | pos.StartLine = startToken.Position.StartLine 128 | pos.EndLine = endToken.Position.EndLine 129 | pos.StartPos = startToken.Position.StartPos 130 | pos.EndPos = endToken.Position.EndPos 131 | 132 | return pos 133 | } 134 | 135 | // NewTokenNodePosition returns new Position 136 | func (b *Builder) NewTokenNodePosition(t *token.Token, n ast.Vertex) *position.Position { 137 | pos := b.pool.Get() 138 | 139 | pos.StartLine = t.Position.StartLine 140 | pos.EndLine = getNodeEndPos(n).endLine 141 | pos.StartPos = t.Position.StartPos 142 | pos.EndPos = getNodeEndPos(n).endPos 143 | 144 | return pos 145 | } 146 | 147 | // NewNodeTokenPosition returns new Position 148 | func (b *Builder) NewNodeTokenPosition(n ast.Vertex, t *token.Token) *position.Position { 149 | pos := b.pool.Get() 150 | 151 | pos.StartLine = getNodeStartPos(n).startLine 152 | pos.EndLine = t.Position.EndLine 153 | pos.StartPos = getNodeStartPos(n).startPos 154 | pos.EndPos = t.Position.EndPos 155 | 156 | return pos 157 | } 158 | 159 | // NewNodesPosition returns new Position 160 | func (b *Builder) NewNodesPosition(startNode ast.Vertex, endNode ast.Vertex) *position.Position { 161 | pos := b.pool.Get() 162 | 163 | pos.StartLine = getNodeStartPos(startNode).startLine 164 | pos.EndLine = getNodeEndPos(endNode).endLine 165 | pos.StartPos = getNodeStartPos(startNode).startPos 166 | pos.EndPos = getNodeEndPos(endNode).endPos 167 | 168 | return pos 169 | } 170 | 171 | // NewNodeListTokenPosition returns new Position 172 | func (b *Builder) NewNodeListTokenPosition(list []ast.Vertex, t *token.Token) *position.Position { 173 | pos := b.pool.Get() 174 | 175 | pos.StartLine = getListStartPos(list).startLine 176 | pos.EndLine = t.Position.EndLine 177 | pos.StartPos = getListStartPos(list).startPos 178 | pos.EndPos = t.Position.EndPos 179 | 180 | return pos 181 | } 182 | 183 | // NewTokenNodeListPosition returns new Position 184 | func (b *Builder) NewTokenNodeListPosition(t *token.Token, list []ast.Vertex) *position.Position { 185 | pos := b.pool.Get() 186 | 187 | pos.StartLine = t.Position.StartLine 188 | pos.EndLine = getListEndPos(list).endLine 189 | pos.StartPos = t.Position.StartPos 190 | pos.EndPos = getListEndPos(list).endPos 191 | 192 | return pos 193 | } 194 | 195 | // NewNodeNodeListPosition returns new Position 196 | func (b *Builder) NewNodeNodeListPosition(n ast.Vertex, list []ast.Vertex) *position.Position { 197 | pos := b.pool.Get() 198 | 199 | pos.StartLine = getNodeStartPos(n).startLine 200 | pos.EndLine = getListEndPos(list).endLine 201 | pos.StartPos = getNodeStartPos(n).startPos 202 | pos.EndPos = getListEndPos(list).endPos 203 | 204 | return pos 205 | } 206 | 207 | // NewNodeListNodePosition returns new Position 208 | func (b *Builder) NewNodeListNodePosition(list []ast.Vertex, n ast.Vertex) *position.Position { 209 | pos := b.pool.Get() 210 | 211 | pos.StartLine = getListStartPos(list).startLine 212 | pos.EndLine = getNodeEndPos(n).endLine 213 | pos.StartPos = getListStartPos(list).startPos 214 | pos.EndPos = getNodeEndPos(n).endPos 215 | 216 | return pos 217 | } 218 | 219 | // NewOptionalListTokensPosition returns new Position 220 | func (b *Builder) NewOptionalListTokensPosition(list []ast.Vertex, t *token.Token, endToken *token.Token) *position.Position { 221 | pos := b.pool.Get() 222 | 223 | if list == nil { 224 | pos.StartLine = t.Position.StartLine 225 | pos.EndLine = endToken.Position.EndLine 226 | pos.StartPos = t.Position.StartPos 227 | pos.EndPos = endToken.Position.EndPos 228 | 229 | return pos 230 | } 231 | pos.StartLine = getListStartPos(list).startLine 232 | pos.EndLine = endToken.Position.EndLine 233 | pos.StartPos = getListStartPos(list).startPos 234 | pos.EndPos = endToken.Position.EndPos 235 | 236 | return pos 237 | } 238 | -------------------------------------------------------------------------------- /internal/position/position_test.go: -------------------------------------------------------------------------------- 1 | package position_test 2 | 3 | import ( 4 | "gotest.tools/assert" 5 | "testing" 6 | 7 | builder "github.com/z7zmey/php-parser/internal/position" 8 | "github.com/z7zmey/php-parser/pkg/ast" 9 | "github.com/z7zmey/php-parser/pkg/position" 10 | "github.com/z7zmey/php-parser/pkg/token" 11 | ) 12 | 13 | func TestNewTokenPosition(t *testing.T) { 14 | tkn := &token.Token{ 15 | Value: []byte(`foo`), 16 | Position: &position.Position{ 17 | StartLine: 1, 18 | EndLine: 1, 19 | StartPos: 0, 20 | EndPos: 3, 21 | }, 22 | } 23 | 24 | pos := builder.NewBuilder().NewTokenPosition(tkn) 25 | 26 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 1, EndPos: 3}, pos) 27 | } 28 | 29 | func TestNewTokensPosition(t *testing.T) { 30 | token1 := &token.Token{ 31 | Value: []byte(`foo`), 32 | Position: &position.Position{ 33 | StartLine: 1, 34 | EndLine: 1, 35 | StartPos: 0, 36 | EndPos: 3, 37 | }, 38 | } 39 | token2 := &token.Token{ 40 | Value: []byte(`foo`), 41 | Position: &position.Position{ 42 | StartLine: 2, 43 | EndLine: 2, 44 | StartPos: 4, 45 | EndPos: 6, 46 | }, 47 | } 48 | 49 | pos := builder.NewBuilder().NewTokensPosition(token1, token2) 50 | 51 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 6}, pos) 52 | } 53 | 54 | func TestNewNodePosition(t *testing.T) { 55 | n := &ast.Identifier{ 56 | Position: &position.Position{ 57 | StartLine: 1, 58 | EndLine: 1, 59 | StartPos: 0, 60 | EndPos: 3, 61 | }, 62 | } 63 | 64 | pos := builder.NewBuilder().NewNodePosition(n) 65 | 66 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 1, EndPos: 3}, pos) 67 | } 68 | 69 | func TestNewTokenNodePosition(t *testing.T) { 70 | tkn := &token.Token{ 71 | Value: []byte(`foo`), 72 | Position: &position.Position{ 73 | StartLine: 1, 74 | EndLine: 1, 75 | StartPos: 0, 76 | EndPos: 3, 77 | }, 78 | } 79 | n := &ast.Identifier{ 80 | Position: &position.Position{ 81 | StartLine: 2, 82 | EndLine: 2, 83 | StartPos: 4, 84 | EndPos: 12, 85 | }, 86 | } 87 | 88 | pos := builder.NewBuilder().NewTokenNodePosition(tkn, n) 89 | 90 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 12}, pos) 91 | } 92 | 93 | func TestNewNodeTokenPosition(t *testing.T) { 94 | n := &ast.Identifier{ 95 | Position: &position.Position{ 96 | StartLine: 1, 97 | EndLine: 1, 98 | StartPos: 0, 99 | EndPos: 9, 100 | }, 101 | } 102 | 103 | tkn := &token.Token{ 104 | Value: []byte(`foo`), 105 | Position: &position.Position{ 106 | StartLine: 2, 107 | EndLine: 2, 108 | StartPos: 10, 109 | EndPos: 12, 110 | }, 111 | } 112 | 113 | pos := builder.NewBuilder().NewNodeTokenPosition(n, tkn) 114 | 115 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 12}, pos) 116 | } 117 | 118 | func TestNewNodeListPosition(t *testing.T) { 119 | n1 := &ast.Identifier{ 120 | Position: &position.Position{ 121 | StartLine: 1, 122 | EndLine: 1, 123 | StartPos: 0, 124 | EndPos: 9, 125 | }, 126 | } 127 | 128 | n2 := &ast.Identifier{ 129 | Position: &position.Position{ 130 | StartLine: 2, 131 | EndLine: 2, 132 | StartPos: 10, 133 | EndPos: 19, 134 | }, 135 | } 136 | 137 | pos := builder.NewBuilder().NewNodeListPosition([]ast.Vertex{n1, n2}) 138 | 139 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 19}, pos) 140 | } 141 | 142 | func TestNewNodesPosition(t *testing.T) { 143 | n1 := &ast.Identifier{ 144 | Position: &position.Position{ 145 | StartLine: 1, 146 | EndLine: 1, 147 | StartPos: 0, 148 | EndPos: 9, 149 | }, 150 | } 151 | 152 | n2 := &ast.Identifier{ 153 | Position: &position.Position{ 154 | StartLine: 2, 155 | EndLine: 2, 156 | StartPos: 10, 157 | EndPos: 19, 158 | }, 159 | } 160 | 161 | pos := builder.NewBuilder().NewNodesPosition(n1, n2) 162 | 163 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 19}, pos) 164 | } 165 | 166 | func TestNewNodeListTokenPosition(t *testing.T) { 167 | n1 := &ast.Identifier{ 168 | Position: &position.Position{ 169 | StartLine: 1, 170 | EndLine: 1, 171 | StartPos: 0, 172 | EndPos: 9, 173 | }, 174 | } 175 | 176 | n2 := &ast.Identifier{ 177 | Position: &position.Position{ 178 | StartLine: 2, 179 | EndLine: 2, 180 | StartPos: 10, 181 | EndPos: 19, 182 | }, 183 | } 184 | 185 | tkn := &token.Token{ 186 | Value: []byte(`foo`), 187 | Position: &position.Position{ 188 | StartLine: 3, 189 | EndLine: 3, 190 | StartPos: 20, 191 | EndPos: 22, 192 | }, 193 | } 194 | 195 | pos := builder.NewBuilder().NewNodeListTokenPosition([]ast.Vertex{n1, n2}, tkn) 196 | 197 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 22}, pos) 198 | } 199 | 200 | func TestNewTokenNodeListPosition(t *testing.T) { 201 | tkn := &token.Token{ 202 | Value: []byte(`foo`), 203 | Position: &position.Position{ 204 | StartLine: 1, 205 | EndLine: 1, 206 | StartPos: 0, 207 | EndPos: 2, 208 | }, 209 | } 210 | 211 | n1 := &ast.Identifier{ 212 | Position: &position.Position{ 213 | StartLine: 2, 214 | EndLine: 2, 215 | StartPos: 3, 216 | EndPos: 10, 217 | }, 218 | } 219 | 220 | n2 := &ast.Identifier{ 221 | Position: &position.Position{ 222 | StartLine: 3, 223 | EndLine: 3, 224 | StartPos: 11, 225 | EndPos: 20, 226 | }, 227 | } 228 | 229 | pos := builder.NewBuilder().NewTokenNodeListPosition(tkn, []ast.Vertex{n1, n2}) 230 | 231 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 20}, pos) 232 | } 233 | 234 | func TestNewNodeNodeListPosition(t *testing.T) { 235 | n1 := &ast.Identifier{ 236 | Position: &position.Position{ 237 | StartLine: 1, 238 | EndLine: 1, 239 | StartPos: 0, 240 | EndPos: 8, 241 | }, 242 | } 243 | 244 | n2 := &ast.Identifier{ 245 | Position: &position.Position{ 246 | StartLine: 2, 247 | EndLine: 2, 248 | StartPos: 9, 249 | EndPos: 17, 250 | }, 251 | } 252 | 253 | n3 := &ast.Identifier{ 254 | Position: &position.Position{ 255 | StartLine: 3, 256 | EndLine: 3, 257 | StartPos: 18, 258 | EndPos: 26, 259 | }, 260 | } 261 | 262 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, []ast.Vertex{n2, n3}) 263 | 264 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 26}, pos) 265 | } 266 | 267 | func TestNewNodeListNodePosition(t *testing.T) { 268 | n1 := &ast.Identifier{ 269 | Position: &position.Position{ 270 | StartLine: 1, 271 | EndLine: 1, 272 | StartPos: 0, 273 | EndPos: 8, 274 | }, 275 | } 276 | n2 := &ast.Identifier{ 277 | Position: &position.Position{ 278 | StartLine: 2, 279 | EndLine: 2, 280 | StartPos: 9, 281 | EndPos: 17, 282 | }, 283 | } 284 | n3 := &ast.Identifier{ 285 | Position: &position.Position{ 286 | StartLine: 3, 287 | EndLine: 3, 288 | StartPos: 18, 289 | EndPos: 26, 290 | }, 291 | } 292 | 293 | pos := builder.NewBuilder().NewNodeListNodePosition([]ast.Vertex{n1, n2}, n3) 294 | 295 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 26}, pos) 296 | } 297 | 298 | func TestNewOptionalListTokensPosition(t *testing.T) { 299 | token1 := &token.Token{ 300 | Value: []byte(`foo`), 301 | Position: &position.Position{ 302 | StartLine: 1, 303 | EndLine: 1, 304 | StartPos: 0, 305 | EndPos: 3, 306 | }, 307 | } 308 | token2 := &token.Token{ 309 | Value: []byte(`foo`), 310 | Position: &position.Position{ 311 | StartLine: 2, 312 | EndLine: 2, 313 | StartPos: 4, 314 | EndPos: 6, 315 | }, 316 | } 317 | 318 | pos := builder.NewBuilder().NewOptionalListTokensPosition(nil, token1, token2) 319 | 320 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 6}, pos) 321 | } 322 | 323 | func TestNewOptionalListTokensPosition2(t *testing.T) { 324 | n2 := &ast.Identifier{ 325 | Position: &position.Position{ 326 | StartLine: 2, 327 | EndLine: 2, 328 | StartPos: 9, 329 | EndPos: 17, 330 | }, 331 | } 332 | n3 := &ast.Identifier{ 333 | Position: &position.Position{ 334 | StartLine: 3, 335 | EndLine: 3, 336 | StartPos: 18, 337 | EndPos: 26, 338 | }, 339 | } 340 | 341 | token1 := &token.Token{ 342 | Value: []byte(`foo`), 343 | Position: &position.Position{ 344 | StartLine: 4, 345 | EndLine: 4, 346 | StartPos: 27, 347 | EndPos: 29, 348 | }, 349 | } 350 | token2 := &token.Token{ 351 | Value: []byte(`foo`), 352 | Position: &position.Position{ 353 | StartLine: 5, 354 | EndLine: 5, 355 | StartPos: 30, 356 | EndPos: 32, 357 | }, 358 | } 359 | 360 | pos := builder.NewBuilder().NewOptionalListTokensPosition([]ast.Vertex{n2, n3}, token1, token2) 361 | 362 | assert.DeepEqual(t, &position.Position{StartLine: 2, EndLine: 5, StartPos: 9, EndPos: 32}, pos) 363 | } 364 | 365 | func TestNilNodePos(t *testing.T) { 366 | pos := builder.NewBuilder().NewNodesPosition(nil, nil) 367 | 368 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: -1, StartPos: -1, EndPos: -1}, pos) 369 | } 370 | 371 | func TestNilNodeListPos(t *testing.T) { 372 | n1 := &ast.Identifier{ 373 | Position: &position.Position{ 374 | StartLine: 1, 375 | EndLine: 1, 376 | StartPos: 0, 377 | EndPos: 8, 378 | }, 379 | } 380 | 381 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, nil) 382 | 383 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: -1, EndPos: -1}, pos) 384 | } 385 | 386 | func TestNilNodeListTokenPos(t *testing.T) { 387 | tkn := &token.Token{ 388 | Value: []byte(`foo`), 389 | Position: &position.Position{ 390 | StartLine: 1, 391 | EndLine: 1, 392 | StartPos: 0, 393 | EndPos: 3, 394 | }, 395 | } 396 | 397 | pos := builder.NewBuilder().NewNodeListTokenPosition(nil, tkn) 398 | 399 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: 1, StartPos: -1, EndPos: 3}, pos) 400 | } 401 | 402 | func TestEmptyNodeListPos(t *testing.T) { 403 | n1 := &ast.Identifier{ 404 | Position: &position.Position{ 405 | StartLine: 1, 406 | EndLine: 1, 407 | StartPos: 0, 408 | EndPos: 8, 409 | }, 410 | } 411 | 412 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, []ast.Vertex{}) 413 | 414 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: -1, EndPos: -1}, pos) 415 | } 416 | 417 | func TestEmptyNodeListTokenPos(t *testing.T) { 418 | tkn := &token.Token{ 419 | Value: []byte(`foo`), 420 | Position: &position.Position{ 421 | StartLine: 1, 422 | EndLine: 1, 423 | StartPos: 0, 424 | EndPos: 3, 425 | }, 426 | } 427 | 428 | pos := builder.NewBuilder().NewNodeListTokenPosition([]ast.Vertex{}, tkn) 429 | 430 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: 1, StartPos: -1, EndPos: 3}, pos) 431 | } 432 | -------------------------------------------------------------------------------- /internal/scanner/lexer.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | 7 | "github.com/z7zmey/php-parser/pkg/conf" 8 | "github.com/z7zmey/php-parser/pkg/errors" 9 | "github.com/z7zmey/php-parser/pkg/position" 10 | "github.com/z7zmey/php-parser/pkg/token" 11 | "github.com/z7zmey/php-parser/pkg/version" 12 | ) 13 | 14 | type Lexer struct { 15 | data []byte 16 | phpVersion *version.Version 17 | errHandlerFunc func(*errors.Error) 18 | 19 | p, pe, cs int 20 | ts, te, act int 21 | stack []int 22 | top int 23 | 24 | heredocLabel []byte 25 | tokenPool *token.Pool 26 | positionPool *position.Pool 27 | newLines NewLines 28 | } 29 | 30 | func NewLexer(data []byte, config conf.Config) *Lexer { 31 | lex := &Lexer{ 32 | data: data, 33 | phpVersion: config.Version, 34 | errHandlerFunc: config.ErrorHandlerFunc, 35 | 36 | pe: len(data), 37 | stack: make([]int, 0), 38 | 39 | tokenPool: token.NewPool(position.DefaultBlockSize), 40 | positionPool: position.NewPool(token.DefaultBlockSize), 41 | newLines: NewLines{make([]int, 0, 128)}, 42 | } 43 | 44 | initLexer(lex) 45 | 46 | return lex 47 | } 48 | 49 | func (lex *Lexer) setTokenPosition(token *token.Token) { 50 | pos := lex.positionPool.Get() 51 | 52 | pos.StartLine = lex.newLines.GetLine(lex.ts) 53 | pos.EndLine = lex.newLines.GetLine(lex.te - 1) 54 | pos.StartPos = lex.ts 55 | pos.EndPos = lex.te 56 | 57 | token.Position = pos 58 | } 59 | 60 | func (lex *Lexer) addFreeFloatingToken(t *token.Token, id token.ID, ps, pe int) { 61 | skippedTkn := lex.tokenPool.Get() 62 | skippedTkn.ID = id 63 | skippedTkn.Value = lex.data[ps:pe] 64 | 65 | lex.setTokenPosition(skippedTkn) 66 | 67 | if t.FreeFloating == nil { 68 | t.FreeFloating = make([]*token.Token, 0, 2) 69 | } 70 | 71 | t.FreeFloating = append(t.FreeFloating, skippedTkn) 72 | } 73 | 74 | func (lex *Lexer) isNotStringVar() bool { 75 | p := lex.p 76 | if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' { 77 | return true 78 | } 79 | 80 | if len(lex.data) < p+1 { 81 | return true 82 | } 83 | 84 | if lex.data[p] == '$' && (lex.data[p+1] == '{' || isValidVarNameStart(lex.data[p+1])) { 85 | return false 86 | } 87 | 88 | if lex.data[p] == '{' && lex.data[p+1] == '$' { 89 | return false 90 | } 91 | 92 | return true 93 | } 94 | 95 | func (lex *Lexer) isNotStringEnd(s byte) bool { 96 | p := lex.p 97 | if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' { 98 | return true 99 | } 100 | 101 | return !(lex.data[p] == s) 102 | } 103 | 104 | func (lex *Lexer) isHeredocEnd(p int) bool { 105 | o, err := version.New("7.3") 106 | if err != nil { 107 | panic(err) 108 | } 109 | 110 | if lex.phpVersion.GreaterOrEqual(o) { 111 | return lex.isHeredocEndSince73(p) 112 | } 113 | 114 | return lex.isHeredocEndBefore73(p) 115 | } 116 | 117 | func (lex *Lexer) isHeredocEndBefore73(p int) bool { 118 | if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' { 119 | return false 120 | } 121 | 122 | l := len(lex.heredocLabel) 123 | if len(lex.data) < p+l { 124 | return false 125 | } 126 | 127 | if len(lex.data) > p+l && lex.data[p+l] != ';' && lex.data[p+l] != '\r' && lex.data[p+l] != '\n' { 128 | return false 129 | } 130 | 131 | if len(lex.data) > p+l+1 && lex.data[p+l] == ';' && lex.data[p+l+1] != '\r' && lex.data[p+l+1] != '\n' { 132 | return false 133 | } 134 | 135 | return bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) 136 | } 137 | 138 | func (lex *Lexer) isHeredocEndSince73(p int) bool { 139 | if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' { 140 | return false 141 | } 142 | 143 | if p == len(lex.data) { 144 | return false 145 | } 146 | 147 | for lex.data[p] == ' ' || lex.data[p] == '\t' { 148 | p++ 149 | } 150 | 151 | l := len(lex.heredocLabel) 152 | if len(lex.data) < p+l { 153 | return false 154 | } 155 | 156 | if len(lex.data) > p+l && isValidVarName(lex.data[p+l]) { 157 | return false 158 | } 159 | 160 | a := string(lex.heredocLabel) 161 | b := string(lex.data[p : p+l]) 162 | 163 | _, _ = a, b 164 | 165 | if bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) { 166 | lex.p = p 167 | return true 168 | } 169 | 170 | return false 171 | } 172 | 173 | func (lex *Lexer) isNotHeredocEnd(p int) bool { 174 | return !lex.isHeredocEnd(p) 175 | } 176 | 177 | func (lex *Lexer) growCallStack() { 178 | if lex.top == len(lex.stack) { 179 | lex.stack = append(lex.stack, 0) 180 | } 181 | } 182 | 183 | func (lex *Lexer) isNotPhpCloseToken() bool { 184 | if lex.p+1 == len(lex.data) { 185 | return true 186 | } 187 | 188 | return lex.data[lex.p] != '?' || lex.data[lex.p+1] != '>' 189 | } 190 | 191 | func (lex *Lexer) isNotNewLine() bool { 192 | if lex.data[lex.p] == '\n' && lex.data[lex.p-1] == '\r' { 193 | return true 194 | } 195 | 196 | return lex.data[lex.p-1] != '\n' && lex.data[lex.p-1] != '\r' 197 | } 198 | 199 | func (lex *Lexer) call(state int, fnext int) { 200 | lex.growCallStack() 201 | 202 | lex.stack[lex.top] = state 203 | lex.top++ 204 | 205 | lex.p++ 206 | lex.cs = fnext 207 | } 208 | 209 | func (lex *Lexer) ret(n int) { 210 | lex.top = lex.top - n 211 | if lex.top < 0 { 212 | lex.top = 0 213 | } 214 | lex.cs = lex.stack[lex.top] 215 | lex.p++ 216 | } 217 | 218 | func (lex *Lexer) ungetStr(s string) { 219 | tokenStr := string(lex.data[lex.ts:lex.te]) 220 | if strings.HasSuffix(tokenStr, s) { 221 | lex.ungetCnt(len(s)) 222 | } 223 | } 224 | 225 | func (lex *Lexer) ungetCnt(n int) { 226 | lex.p = lex.p - n 227 | lex.te = lex.te - n 228 | } 229 | 230 | func (lex *Lexer) error(msg string) { 231 | if lex.errHandlerFunc == nil { 232 | return 233 | } 234 | 235 | pos := position.NewPosition( 236 | lex.newLines.GetLine(lex.ts), 237 | lex.newLines.GetLine(lex.te-1), 238 | lex.ts, 239 | lex.te, 240 | ) 241 | 242 | lex.errHandlerFunc(errors.NewError(msg, pos)) 243 | } 244 | 245 | func isValidVarNameStart(r byte) bool { 246 | return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || r >= 0x80 247 | } 248 | 249 | func isValidVarName(r byte) bool { 250 | return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r >= 0x80 251 | } 252 | -------------------------------------------------------------------------------- /internal/scanner/newline.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | type NewLines struct { 4 | data []int 5 | } 6 | 7 | func (nl *NewLines) Append(p int) { 8 | if len(nl.data) == 0 || nl.data[len(nl.data)-1] < p { 9 | nl.data = append(nl.data, p) 10 | } 11 | } 12 | 13 | func (nl *NewLines) GetLine(p int) int { 14 | line := len(nl.data) + 1 15 | 16 | for i := len(nl.data) - 1; i >= 0; i-- { 17 | if p < nl.data[i] { 18 | line = i + 1 19 | } else { 20 | break 21 | } 22 | } 23 | 24 | return line 25 | } 26 | -------------------------------------------------------------------------------- /internal/scanner/scanner.rl: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/z7zmey/php-parser/pkg/token" 9 | ) 10 | 11 | %%{ 12 | machine lexer; 13 | write data; 14 | access lex.; 15 | variable p lex.p; 16 | variable pe lex.pe; 17 | }%% 18 | 19 | func initLexer(lex *Lexer) { 20 | %% write init; 21 | } 22 | 23 | func (lex *Lexer) Lex() *token.Token { 24 | eof := lex.pe 25 | var tok token.ID 26 | 27 | tkn := lex.tokenPool.Get() 28 | 29 | lblStart := 0 30 | lblEnd := 0 31 | 32 | _, _ = lblStart, lblEnd 33 | 34 | %%{ 35 | action heredoc_lbl_start {lblStart = lex.p} 36 | action heredoc_lbl_end {lblEnd = lex.p} 37 | 38 | action new_line { 39 | if lex.data[lex.p] == '\n' { 40 | lex.newLines.Append(lex.p+1) 41 | } 42 | 43 | if lex.data[lex.p] == '\r' && lex.data[lex.p+1] != '\n' { 44 | lex.newLines.Append(lex.p+1) 45 | } 46 | } 47 | 48 | action is_not_heredoc_end { lex.isNotHeredocEnd(lex.p) } 49 | action is_not_comment_end { lex.isNotPhpCloseToken() && lex.isNotNewLine() } 50 | action is_not_heredoc_end_or_var { lex.isNotHeredocEnd(lex.p) && lex.isNotStringVar() } 51 | action is_not_string_end_or_var { lex.isNotStringEnd('"') && lex.isNotStringVar() } 52 | action is_not_backqoute_end_or_var { lex.isNotStringEnd('`') && lex.isNotStringVar() } 53 | 54 | newline = ('\r\n' >(nl, 1) | '\r' >(nl, 0) | '\n' >(nl, 0)) $new_line %{}; 55 | any_line = any | newline; 56 | whitespace = [\t\v\f ]; 57 | whitespace_line = [\t\v\f ] | newline; 58 | 59 | lnum = [0-9]+('_'[0-9]+)*; 60 | dnum = (lnum?"." lnum)|(lnum"."lnum?); 61 | hnum = '0x'[0-9a-fA-F]+('_'[0-9a-fA-F]+)*; 62 | bnum = '0b'[01]+('_'[01]+)*; 63 | 64 | exponent_dnum = (lnum | dnum) ('e'|'E') ('+'|'-')? lnum; 65 | varname_first = [a-zA-Z_] | (0x0080..0x00FF); 66 | varname_second = varname_first | [0-9]; 67 | varname = varname_first (varname_second)*; 68 | heredoc_label = varname >heredoc_lbl_start %heredoc_lbl_end; 69 | operators = ';'|':'|','|'.'|'['|']'|'('|')'|'|'|'/'|'^'|'&'|'+'|'-'|'*'|'='|'%'|'!'|'~'|'$'|'<'|'>'|'?'|'@'; 70 | 71 | prepush { lex.growCallStack(); } 72 | 73 | constant_string = 74 | start: ( 75 | "'" -> qoute 76 | | "b"i? '"' -> double_qoute 77 | ), 78 | 79 | # single qoute string 80 | 81 | qoute: ( 82 | (any - [\\'\r\n]) -> qoute 83 | | "\r" @new_line -> qoute 84 | | "\n" @new_line -> qoute 85 | | "\\" -> qoute_any 86 | | "'" -> final 87 | ), 88 | qoute_any: ( 89 | (any - [\r\n]) -> qoute 90 | | "\r" @new_line -> qoute 91 | | "\n" @new_line -> qoute 92 | ), 93 | 94 | # double qoute string 95 | 96 | double_qoute: ( 97 | (any - [\\"${\r\n]) -> double_qoute 98 | | "\r" @new_line -> double_qoute 99 | | "\n" @new_line -> double_qoute 100 | | "\\" -> double_qoute_any 101 | | '"' -> final 102 | | '$' -> double_qoute_nonvarname 103 | | '{' -> double_qoute_nondollar 104 | ), 105 | double_qoute_any: ( 106 | (any - [\r\n]) -> double_qoute 107 | | "\r" @new_line -> double_qoute 108 | | "\n" @new_line -> double_qoute 109 | ), 110 | double_qoute_nondollar: ( 111 | (any - [\\$"\r\n]) -> double_qoute 112 | | "\r" @new_line -> double_qoute 113 | | "\n" @new_line -> double_qoute 114 | | "\\" -> double_qoute_any 115 | | '"' -> final 116 | ), 117 | double_qoute_nonvarname: ( 118 | (any - [\\${"\r\n] - varname_first) -> double_qoute 119 | | "\r" @new_line -> double_qoute 120 | | "\n" @new_line -> double_qoute 121 | | "\\" -> double_qoute_any 122 | | '$' -> double_qoute_nonvarname 123 | | '"' -> final 124 | ); 125 | 126 | main := |* 127 | "#!" any* :>> newline => { 128 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) 129 | }; 130 | any => { 131 | fnext html; 132 | lex.ungetCnt(1) 133 | }; 134 | *|; 135 | 136 | html := |* 137 | any_line+ -- ' { 138 | lex.ungetStr("<") 139 | lex.setTokenPosition(tkn) 140 | tok = token.T_INLINE_HTML; 141 | fbreak; 142 | }; 143 | ' { 144 | lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.te) 145 | fnext php; 146 | }; 147 | ' { 148 | lex.ungetCnt(lex.te - lex.ts - 5) 149 | lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.ts+5) 150 | fnext php; 151 | }; 152 | ' { 153 | lex.setTokenPosition(tkn); 154 | tok = token.T_ECHO; 155 | fnext php; 156 | fbreak; 157 | }; 158 | *|; 159 | 160 | php := |* 161 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; 162 | '?>' newline? => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;}; 163 | ';' whitespace_line* '?>' newline? => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;}; 164 | 165 | (dnum | exponent_dnum) => {lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;}; 166 | bnum => { 167 | s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1) 168 | _, err := strconv.ParseInt(s, 2, 0) 169 | 170 | if err == nil { 171 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; 172 | } 173 | 174 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; 175 | }; 176 | lnum => { 177 | base := 10 178 | if lex.data[lex.ts] == '0' { 179 | base = 8 180 | } 181 | 182 | s := strings.Replace(string(lex.data[lex.ts:lex.te]), "_", "", -1) 183 | _, err := strconv.ParseInt(s, base, 0) 184 | 185 | if err == nil { 186 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; 187 | } 188 | 189 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; 190 | }; 191 | hnum => { 192 | s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1) 193 | _, err := strconv.ParseInt(s, 16, 0) 194 | 195 | if err == nil { 196 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; 197 | } 198 | 199 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; 200 | }; 201 | 202 | 'abstract'i => {lex.setTokenPosition(tkn); tok = token.T_ABSTRACT; fbreak;}; 203 | 'array'i => {lex.setTokenPosition(tkn); tok = token.T_ARRAY; fbreak;}; 204 | 'as'i => {lex.setTokenPosition(tkn); tok = token.T_AS; fbreak;}; 205 | 'break'i => {lex.setTokenPosition(tkn); tok = token.T_BREAK; fbreak;}; 206 | 'callable'i => {lex.setTokenPosition(tkn); tok = token.T_CALLABLE; fbreak;}; 207 | 'case'i => {lex.setTokenPosition(tkn); tok = token.T_CASE; fbreak;}; 208 | 'catch'i => {lex.setTokenPosition(tkn); tok = token.T_CATCH; fbreak;}; 209 | 'class'i => {lex.setTokenPosition(tkn); tok = token.T_CLASS; fbreak;}; 210 | 'clone'i => {lex.setTokenPosition(tkn); tok = token.T_CLONE; fbreak;}; 211 | 'const'i => {lex.setTokenPosition(tkn); tok = token.T_CONST; fbreak;}; 212 | 'continue'i => {lex.setTokenPosition(tkn); tok = token.T_CONTINUE; fbreak;}; 213 | 'declare'i => {lex.setTokenPosition(tkn); tok = token.T_DECLARE; fbreak;}; 214 | 'default'i => {lex.setTokenPosition(tkn); tok = token.T_DEFAULT; fbreak;}; 215 | 'do'i => {lex.setTokenPosition(tkn); tok = token.T_DO; fbreak;}; 216 | 'echo'i => {lex.setTokenPosition(tkn); tok = token.T_ECHO; fbreak;}; 217 | 'else'i => {lex.setTokenPosition(tkn); tok = token.T_ELSE; fbreak;}; 218 | 'elseif'i => {lex.setTokenPosition(tkn); tok = token.T_ELSEIF; fbreak;}; 219 | 'empty'i => {lex.setTokenPosition(tkn); tok = token.T_EMPTY; fbreak;}; 220 | 'enddeclare'i => {lex.setTokenPosition(tkn); tok = token.T_ENDDECLARE; fbreak;}; 221 | 'endfor'i => {lex.setTokenPosition(tkn); tok = token.T_ENDFOR; fbreak;}; 222 | 'endforeach'i => {lex.setTokenPosition(tkn); tok = token.T_ENDFOREACH; fbreak;}; 223 | 'endif'i => {lex.setTokenPosition(tkn); tok = token.T_ENDIF; fbreak;}; 224 | 'endswitch'i => {lex.setTokenPosition(tkn); tok = token.T_ENDSWITCH; fbreak;}; 225 | 'endwhile'i => {lex.setTokenPosition(tkn); tok = token.T_ENDWHILE; fbreak;}; 226 | 'eval'i => {lex.setTokenPosition(tkn); tok = token.T_EVAL; fbreak;}; 227 | 'exit'i | 'die'i => {lex.setTokenPosition(tkn); tok = token.T_EXIT; fbreak;}; 228 | 'extends'i => {lex.setTokenPosition(tkn); tok = token.T_EXTENDS; fbreak;}; 229 | 'final'i => {lex.setTokenPosition(tkn); tok = token.T_FINAL; fbreak;}; 230 | 'finally'i => {lex.setTokenPosition(tkn); tok = token.T_FINALLY; fbreak;}; 231 | 'for'i => {lex.setTokenPosition(tkn); tok = token.T_FOR; fbreak;}; 232 | 'foreach'i => {lex.setTokenPosition(tkn); tok = token.T_FOREACH; fbreak;}; 233 | 'function'i | 'cfunction'i => {lex.setTokenPosition(tkn); tok = token.T_FUNCTION; fbreak;}; 234 | 'fn'i => {lex.setTokenPosition(tkn); tok = token.T_FN; fbreak;}; 235 | 'global'i => {lex.setTokenPosition(tkn); tok = token.T_GLOBAL; fbreak;}; 236 | 'goto'i => {lex.setTokenPosition(tkn); tok = token.T_GOTO; fbreak;}; 237 | 'if'i => {lex.setTokenPosition(tkn); tok = token.T_IF; fbreak;}; 238 | 'isset'i => {lex.setTokenPosition(tkn); tok = token.T_ISSET; fbreak;}; 239 | 'implements'i => {lex.setTokenPosition(tkn); tok = token.T_IMPLEMENTS; fbreak;}; 240 | 'instanceof'i => {lex.setTokenPosition(tkn); tok = token.T_INSTANCEOF; fbreak;}; 241 | 'insteadof'i => {lex.setTokenPosition(tkn); tok = token.T_INSTEADOF; fbreak;}; 242 | 'interface'i => {lex.setTokenPosition(tkn); tok = token.T_INTERFACE; fbreak;}; 243 | 'list'i => {lex.setTokenPosition(tkn); tok = token.T_LIST; fbreak;}; 244 | 'namespace'i => {lex.setTokenPosition(tkn); tok = token.T_NAMESPACE; fbreak;}; 245 | 'private'i => {lex.setTokenPosition(tkn); tok = token.T_PRIVATE; fbreak;}; 246 | 'public'i => {lex.setTokenPosition(tkn); tok = token.T_PUBLIC; fbreak;}; 247 | 'print'i => {lex.setTokenPosition(tkn); tok = token.T_PRINT; fbreak;}; 248 | 'protected'i => {lex.setTokenPosition(tkn); tok = token.T_PROTECTED; fbreak;}; 249 | 'return'i => {lex.setTokenPosition(tkn); tok = token.T_RETURN; fbreak;}; 250 | 'static'i => {lex.setTokenPosition(tkn); tok = token.T_STATIC; fbreak;}; 251 | 'switch'i => {lex.setTokenPosition(tkn); tok = token.T_SWITCH; fbreak;}; 252 | 'throw'i => {lex.setTokenPosition(tkn); tok = token.T_THROW; fbreak;}; 253 | 'trait'i => {lex.setTokenPosition(tkn); tok = token.T_TRAIT; fbreak;}; 254 | 'try'i => {lex.setTokenPosition(tkn); tok = token.T_TRY; fbreak;}; 255 | 'unset'i => {lex.setTokenPosition(tkn); tok = token.T_UNSET; fbreak;}; 256 | 'use'i => {lex.setTokenPosition(tkn); tok = token.T_USE; fbreak;}; 257 | 'var'i => {lex.setTokenPosition(tkn); tok = token.T_VAR; fbreak;}; 258 | 'while'i => {lex.setTokenPosition(tkn); tok = token.T_WHILE; fbreak;}; 259 | 'yield'i whitespace_line+ 'from'i => {lex.setTokenPosition(tkn); tok = token.T_YIELD_FROM; fbreak;}; 260 | 'yield'i => {lex.setTokenPosition(tkn); tok = token.T_YIELD; fbreak;}; 261 | 'include'i => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE; fbreak;}; 262 | 'include_once'i => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE_ONCE; fbreak;}; 263 | 'require'i => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE; fbreak;}; 264 | 'require_once'i => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE_ONCE; fbreak;}; 265 | '__CLASS__'i => {lex.setTokenPosition(tkn); tok = token.T_CLASS_C; fbreak;}; 266 | '__DIR__'i => {lex.setTokenPosition(tkn); tok = token.T_DIR; fbreak;}; 267 | '__FILE__'i => {lex.setTokenPosition(tkn); tok = token.T_FILE; fbreak;}; 268 | '__FUNCTION__'i => {lex.setTokenPosition(tkn); tok = token.T_FUNC_C; fbreak;}; 269 | '__LINE__'i => {lex.setTokenPosition(tkn); tok = token.T_LINE; fbreak;}; 270 | '__NAMESPACE__'i => {lex.setTokenPosition(tkn); tok = token.T_NS_C; fbreak;}; 271 | '__METHOD__'i => {lex.setTokenPosition(tkn); tok = token.T_METHOD_C; fbreak;}; 272 | '__TRAIT__'i => {lex.setTokenPosition(tkn); tok = token.T_TRAIT_C; fbreak;}; 273 | '__halt_compiler'i => {lex.setTokenPosition(tkn); tok = token.T_HALT_COMPILER; fnext halt_compiller_open_parenthesis; fbreak;}; 274 | 'new'i => {lex.setTokenPosition(tkn); tok = token.T_NEW; fbreak;}; 275 | 'and'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_AND; fbreak;}; 276 | 'or'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_OR; fbreak;}; 277 | 'xor'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_XOR; fbreak;}; 278 | '\\' => {lex.setTokenPosition(tkn); tok = token.T_NS_SEPARATOR; fbreak;}; 279 | '...' => {lex.setTokenPosition(tkn); tok = token.T_ELLIPSIS; fbreak;}; 280 | '::' => {lex.setTokenPosition(tkn); tok = token.T_PAAMAYIM_NEKUDOTAYIM; fbreak;}; 281 | '&&' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_AND; fbreak;}; 282 | '||' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_OR; fbreak;}; 283 | '&=' => {lex.setTokenPosition(tkn); tok = token.T_AND_EQUAL; fbreak;}; 284 | '|=' => {lex.setTokenPosition(tkn); tok = token.T_OR_EQUAL; fbreak;}; 285 | '.=' => {lex.setTokenPosition(tkn); tok = token.T_CONCAT_EQUAL; fbreak;}; 286 | '*=' => {lex.setTokenPosition(tkn); tok = token.T_MUL_EQUAL; fbreak;}; 287 | '**=' => {lex.setTokenPosition(tkn); tok = token.T_POW_EQUAL; fbreak;}; 288 | '/=' => {lex.setTokenPosition(tkn); tok = token.T_DIV_EQUAL; fbreak;}; 289 | '+=' => {lex.setTokenPosition(tkn); tok = token.T_PLUS_EQUAL; fbreak;}; 290 | '-=' => {lex.setTokenPosition(tkn); tok = token.T_MINUS_EQUAL; fbreak;}; 291 | '^=' => {lex.setTokenPosition(tkn); tok = token.T_XOR_EQUAL; fbreak;}; 292 | '%=' => {lex.setTokenPosition(tkn); tok = token.T_MOD_EQUAL; fbreak;}; 293 | '--' => {lex.setTokenPosition(tkn); tok = token.T_DEC; fbreak;}; 294 | '++' => {lex.setTokenPosition(tkn); tok = token.T_INC; fbreak;}; 295 | '=>' => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_ARROW; fbreak;}; 296 | '<=>' => {lex.setTokenPosition(tkn); tok = token.T_SPACESHIP; fbreak;}; 297 | '!=' | '<>' => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_EQUAL; fbreak;}; 298 | '!==' => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_IDENTICAL; fbreak;}; 299 | '==' => {lex.setTokenPosition(tkn); tok = token.T_IS_EQUAL; fbreak;}; 300 | '===' => {lex.setTokenPosition(tkn); tok = token.T_IS_IDENTICAL; fbreak;}; 301 | '<<=' => {lex.setTokenPosition(tkn); tok = token.T_SL_EQUAL; fbreak;}; 302 | '>>=' => {lex.setTokenPosition(tkn); tok = token.T_SR_EQUAL; fbreak;}; 303 | '>=' => {lex.setTokenPosition(tkn); tok = token.T_IS_GREATER_OR_EQUAL; fbreak;}; 304 | '<=' => {lex.setTokenPosition(tkn); tok = token.T_IS_SMALLER_OR_EQUAL; fbreak;}; 305 | '**' => {lex.setTokenPosition(tkn); tok = token.T_POW; fbreak;}; 306 | '<<' => {lex.setTokenPosition(tkn); tok = token.T_SL; fbreak;}; 307 | '>>' => {lex.setTokenPosition(tkn); tok = token.T_SR; fbreak;}; 308 | '??' => {lex.setTokenPosition(tkn); tok = token.T_COALESCE; fbreak;}; 309 | '??=' => {lex.setTokenPosition(tkn); tok = token.T_COALESCE_EQUAL; fbreak;}; 310 | 311 | '(' whitespace* 'array'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_ARRAY_CAST; fbreak;}; 312 | '(' whitespace* ('bool'i|'boolean'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_BOOL_CAST; fbreak;}; 313 | '(' whitespace* ('real'i|'double'i|'float'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_CAST; fbreak;}; 314 | '(' whitespace* ('int'i|'integer'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_INT_CAST; fbreak;}; 315 | '(' whitespace* 'object'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_CAST; fbreak;}; 316 | '(' whitespace* ('string'i|'binary'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_STRING_CAST; fbreak;}; 317 | '(' whitespace* 'unset'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_UNSET_CAST; fbreak;}; 318 | 319 | ('#' | '//') any_line* when is_not_comment_end => { 320 | lex.ungetStr("?>") 321 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) 322 | }; 323 | '/*' any_line* :>> '*/' { 324 | isDocComment := false; 325 | if lex.te - lex.ts > 4 && string(lex.data[lex.ts:lex.ts+3]) == "/**" { 326 | isDocComment = true; 327 | } 328 | 329 | if isDocComment { 330 | lex.addFreeFloatingToken(tkn, token.T_DOC_COMMENT, lex.ts, lex.te) 331 | } else { 332 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) 333 | } 334 | }; 335 | 336 | operators => { 337 | lex.setTokenPosition(tkn); 338 | tok = token.ID(int(lex.data[lex.ts])); 339 | fbreak; 340 | }; 341 | 342 | "{" => { lex.setTokenPosition(tkn); tok = token.ID(int('{')); lex.call(ftargs, fentry(php)); goto _out; }; 343 | "}" => { lex.setTokenPosition(tkn); tok = token.ID(int('}')); lex.ret(1); goto _out;}; 344 | "$" varname => { lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak; }; 345 | varname => { lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak; }; 346 | 347 | "->" => { lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fnext property; fbreak; }; 348 | 349 | constant_string => { 350 | lex.setTokenPosition(tkn); 351 | tok = token.T_CONSTANT_ENCAPSED_STRING; 352 | fbreak; 353 | }; 354 | 355 | "b"i? "<<<" [ \t]* ( heredoc_label | ("'" heredoc_label "'") | ('"' heredoc_label '"') ) newline => { 356 | lex.heredocLabel = lex.data[lblStart:lblEnd] 357 | lex.setTokenPosition(tkn); 358 | tok = token.T_START_HEREDOC; 359 | 360 | if lex.isHeredocEnd(lex.p+1) { 361 | fnext heredoc_end; 362 | } else if lex.data[lblStart-1] == '\'' { 363 | fnext nowdoc; 364 | } else { 365 | fnext heredoc; 366 | } 367 | fbreak; 368 | }; 369 | "`" => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext backqote; fbreak;}; 370 | '"' => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext template_string; fbreak;}; 371 | 372 | any_line => { 373 | c := lex.data[lex.p] 374 | lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c)); 375 | }; 376 | *|; 377 | 378 | property := |* 379 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; 380 | "->" => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;}; 381 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fnext php; fbreak;}; 382 | any => {lex.ungetCnt(1); fgoto php;}; 383 | *|; 384 | 385 | nowdoc := |* 386 | any_line* when is_not_heredoc_end => { 387 | lex.setTokenPosition(tkn); 388 | tok = token.T_ENCAPSED_AND_WHITESPACE; 389 | fnext heredoc_end; 390 | fbreak; 391 | }; 392 | *|; 393 | 394 | heredoc := |* 395 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; 396 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; 397 | "$" => {lex.ungetCnt(1); fcall string_var;}; 398 | any_line* when is_not_heredoc_end_or_var => { 399 | lex.setTokenPosition(tkn); 400 | tok = token.T_ENCAPSED_AND_WHITESPACE; 401 | 402 | if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' { 403 | fnext heredoc_end; 404 | } 405 | fbreak; 406 | }; 407 | *|; 408 | 409 | backqote := |* 410 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; 411 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; 412 | "$" varname_first => {lex.ungetCnt(2); fcall string_var;}; 413 | '`' => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext php; fbreak;}; 414 | any_line* when is_not_backqoute_end_or_var => { 415 | lex.setTokenPosition(tkn); 416 | tok = token.T_ENCAPSED_AND_WHITESPACE; 417 | fbreak; 418 | }; 419 | *|; 420 | 421 | template_string := |* 422 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; 423 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; 424 | "$" varname_first => {lex.ungetCnt(2); fcall string_var;}; 425 | '"' => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext php; fbreak;}; 426 | any_line* when is_not_string_end_or_var => { 427 | lex.setTokenPosition(tkn); 428 | tok = token.T_ENCAPSED_AND_WHITESPACE; 429 | fbreak; 430 | }; 431 | *|; 432 | 433 | heredoc_end := |* 434 | varname -- ";" => { 435 | lex.setTokenPosition(tkn); 436 | tok = token.T_END_HEREDOC; 437 | fnext php; 438 | fbreak; 439 | }; 440 | varname => { 441 | lex.setTokenPosition(tkn); 442 | tok = token.T_END_HEREDOC; 443 | fnext php; 444 | fbreak; 445 | }; 446 | *|; 447 | 448 | string_var := |* 449 | '$' varname => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;}; 450 | '->' varname_first => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;}; 451 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;}; 452 | '[' => {lex.setTokenPosition(tkn); tok = token.ID(int('[')); lex.call(ftargs, fentry(string_var_index)); goto _out;}; 453 | any => {lex.ungetCnt(1); fret;}; 454 | *|; 455 | 456 | string_var_index := |* 457 | lnum | hnum | bnum => {lex.setTokenPosition(tkn); tok = token.T_NUM_STRING; fbreak;}; 458 | '$' varname => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;}; 459 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;}; 460 | whitespace_line | [\\'#] => {lex.setTokenPosition(tkn); tok = token.T_ENCAPSED_AND_WHITESPACE; lex.ret(2); goto _out;}; 461 | operators > (svi, 1) => {lex.setTokenPosition(tkn); tok = token.ID(int(lex.data[lex.ts])); fbreak;}; 462 | ']' > (svi, 2) => {lex.setTokenPosition(tkn); tok = token.ID(int(']')); lex.ret(2); goto _out;}; 463 | any_line => { 464 | c := lex.data[lex.p] 465 | lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c)); 466 | }; 467 | *|; 468 | 469 | string_var_name := |* 470 | varname ("[" | "}") => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_STRING_VARNAME; fnext php; fbreak;}; 471 | any => {lex.ungetCnt(1); fnext php;}; 472 | *|; 473 | 474 | halt_compiller_open_parenthesis := |* 475 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; 476 | "(" => {lex.setTokenPosition(tkn); tok = token.ID(int('(')); fnext halt_compiller_close_parenthesis; fbreak;}; 477 | any => {lex.ungetCnt(1); fnext php;}; 478 | *|; 479 | 480 | halt_compiller_close_parenthesis := |* 481 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; 482 | ")" => {lex.setTokenPosition(tkn); tok = token.ID(int(')')); fnext halt_compiller_close_semicolon; fbreak;}; 483 | any => {lex.ungetCnt(1); fnext php;}; 484 | *|; 485 | 486 | halt_compiller_close_semicolon := |* 487 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; 488 | ";" => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext halt_compiller_end; fbreak;}; 489 | any => {lex.ungetCnt(1); fnext php;}; 490 | *|; 491 | 492 | halt_compiller_end := |* 493 | any_line* => { lex.addFreeFloatingToken(tkn, token.T_HALT_COMPILER, lex.ts, lex.te); }; 494 | *|; 495 | 496 | write exec; 497 | }%% 498 | 499 | tkn.Value = lex.data[lex.ts:lex.te] 500 | tkn.ID = token.ID(tok) 501 | 502 | return tkn 503 | } -------------------------------------------------------------------------------- /parser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z7zmey/php-parser/367eff9de651fb9479c98f4f3741db3195e549ad/parser.jpg -------------------------------------------------------------------------------- /pkg/ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import "github.com/z7zmey/php-parser/pkg/position" 4 | 5 | type Vertex interface { 6 | Accept(v Visitor) 7 | GetPosition() *position.Position 8 | } 9 | 10 | type Visitor interface { 11 | Root(n *Root) 12 | Nullable(n *Nullable) 13 | Parameter(n *Parameter) 14 | Identifier(n *Identifier) 15 | Argument(n *Argument) 16 | 17 | StmtBreak(n *StmtBreak) 18 | StmtCase(n *StmtCase) 19 | StmtCatch(n *StmtCatch) 20 | StmtClass(n *StmtClass) 21 | StmtClassConstList(n *StmtClassConstList) 22 | StmtClassMethod(n *StmtClassMethod) 23 | StmtConstList(n *StmtConstList) 24 | StmtConstant(n *StmtConstant) 25 | StmtContinue(n *StmtContinue) 26 | StmtDeclare(n *StmtDeclare) 27 | StmtDefault(n *StmtDefault) 28 | StmtDo(n *StmtDo) 29 | StmtEcho(n *StmtEcho) 30 | StmtElse(n *StmtElse) 31 | StmtElseIf(n *StmtElseIf) 32 | StmtExpression(n *StmtExpression) 33 | StmtFinally(n *StmtFinally) 34 | StmtFor(n *StmtFor) 35 | StmtForeach(n *StmtForeach) 36 | StmtFunction(n *StmtFunction) 37 | StmtGlobal(n *StmtGlobal) 38 | StmtGoto(n *StmtGoto) 39 | StmtHaltCompiler(n *StmtHaltCompiler) 40 | StmtIf(n *StmtIf) 41 | StmtInlineHtml(n *StmtInlineHtml) 42 | StmtInterface(n *StmtInterface) 43 | StmtLabel(n *StmtLabel) 44 | StmtNamespace(n *StmtNamespace) 45 | StmtNop(n *StmtNop) 46 | StmtProperty(n *StmtProperty) 47 | StmtPropertyList(n *StmtPropertyList) 48 | StmtReturn(n *StmtReturn) 49 | StmtStatic(n *StmtStatic) 50 | StmtStaticVar(n *StmtStaticVar) 51 | StmtStmtList(n *StmtStmtList) 52 | StmtSwitch(n *StmtSwitch) 53 | StmtThrow(n *StmtThrow) 54 | StmtTrait(n *StmtTrait) 55 | StmtTraitUse(n *StmtTraitUse) 56 | StmtTraitUseAlias(n *StmtTraitUseAlias) 57 | StmtTraitUsePrecedence(n *StmtTraitUsePrecedence) 58 | StmtTry(n *StmtTry) 59 | StmtUnset(n *StmtUnset) 60 | StmtUse(n *StmtUseList) 61 | StmtGroupUse(n *StmtGroupUseList) 62 | StmtUseDeclaration(n *StmtUse) 63 | StmtWhile(n *StmtWhile) 64 | 65 | ExprArray(n *ExprArray) 66 | ExprArrayDimFetch(n *ExprArrayDimFetch) 67 | ExprArrayItem(n *ExprArrayItem) 68 | ExprArrowFunction(n *ExprArrowFunction) 69 | ExprBrackets(n *ExprBrackets) 70 | ExprBitwiseNot(n *ExprBitwiseNot) 71 | ExprBooleanNot(n *ExprBooleanNot) 72 | ExprClassConstFetch(n *ExprClassConstFetch) 73 | ExprClone(n *ExprClone) 74 | ExprClosure(n *ExprClosure) 75 | ExprClosureUse(n *ExprClosureUse) 76 | ExprConstFetch(n *ExprConstFetch) 77 | ExprEmpty(n *ExprEmpty) 78 | ExprErrorSuppress(n *ExprErrorSuppress) 79 | ExprEval(n *ExprEval) 80 | ExprExit(n *ExprExit) 81 | ExprFunctionCall(n *ExprFunctionCall) 82 | ExprInclude(n *ExprInclude) 83 | ExprIncludeOnce(n *ExprIncludeOnce) 84 | ExprInstanceOf(n *ExprInstanceOf) 85 | ExprIsset(n *ExprIsset) 86 | ExprList(n *ExprList) 87 | ExprMethodCall(n *ExprMethodCall) 88 | ExprNew(n *ExprNew) 89 | ExprPostDec(n *ExprPostDec) 90 | ExprPostInc(n *ExprPostInc) 91 | ExprPreDec(n *ExprPreDec) 92 | ExprPreInc(n *ExprPreInc) 93 | ExprPrint(n *ExprPrint) 94 | ExprPropertyFetch(n *ExprPropertyFetch) 95 | ExprRequire(n *ExprRequire) 96 | ExprRequireOnce(n *ExprRequireOnce) 97 | ExprShellExec(n *ExprShellExec) 98 | ExprStaticCall(n *ExprStaticCall) 99 | ExprStaticPropertyFetch(n *ExprStaticPropertyFetch) 100 | ExprTernary(n *ExprTernary) 101 | ExprUnaryMinus(n *ExprUnaryMinus) 102 | ExprUnaryPlus(n *ExprUnaryPlus) 103 | ExprVariable(n *ExprVariable) 104 | ExprYield(n *ExprYield) 105 | ExprYieldFrom(n *ExprYieldFrom) 106 | 107 | ExprAssign(n *ExprAssign) 108 | ExprAssignReference(n *ExprAssignReference) 109 | ExprAssignBitwiseAnd(n *ExprAssignBitwiseAnd) 110 | ExprAssignBitwiseOr(n *ExprAssignBitwiseOr) 111 | ExprAssignBitwiseXor(n *ExprAssignBitwiseXor) 112 | ExprAssignCoalesce(n *ExprAssignCoalesce) 113 | ExprAssignConcat(n *ExprAssignConcat) 114 | ExprAssignDiv(n *ExprAssignDiv) 115 | ExprAssignMinus(n *ExprAssignMinus) 116 | ExprAssignMod(n *ExprAssignMod) 117 | ExprAssignMul(n *ExprAssignMul) 118 | ExprAssignPlus(n *ExprAssignPlus) 119 | ExprAssignPow(n *ExprAssignPow) 120 | ExprAssignShiftLeft(n *ExprAssignShiftLeft) 121 | ExprAssignShiftRight(n *ExprAssignShiftRight) 122 | 123 | ExprBinaryBitwiseAnd(n *ExprBinaryBitwiseAnd) 124 | ExprBinaryBitwiseOr(n *ExprBinaryBitwiseOr) 125 | ExprBinaryBitwiseXor(n *ExprBinaryBitwiseXor) 126 | ExprBinaryBooleanAnd(n *ExprBinaryBooleanAnd) 127 | ExprBinaryBooleanOr(n *ExprBinaryBooleanOr) 128 | ExprBinaryCoalesce(n *ExprBinaryCoalesce) 129 | ExprBinaryConcat(n *ExprBinaryConcat) 130 | ExprBinaryDiv(n *ExprBinaryDiv) 131 | ExprBinaryEqual(n *ExprBinaryEqual) 132 | ExprBinaryGreater(n *ExprBinaryGreater) 133 | ExprBinaryGreaterOrEqual(n *ExprBinaryGreaterOrEqual) 134 | ExprBinaryIdentical(n *ExprBinaryIdentical) 135 | ExprBinaryLogicalAnd(n *ExprBinaryLogicalAnd) 136 | ExprBinaryLogicalOr(n *ExprBinaryLogicalOr) 137 | ExprBinaryLogicalXor(n *ExprBinaryLogicalXor) 138 | ExprBinaryMinus(n *ExprBinaryMinus) 139 | ExprBinaryMod(n *ExprBinaryMod) 140 | ExprBinaryMul(n *ExprBinaryMul) 141 | ExprBinaryNotEqual(n *ExprBinaryNotEqual) 142 | ExprBinaryNotIdentical(n *ExprBinaryNotIdentical) 143 | ExprBinaryPlus(n *ExprBinaryPlus) 144 | ExprBinaryPow(n *ExprBinaryPow) 145 | ExprBinaryShiftLeft(n *ExprBinaryShiftLeft) 146 | ExprBinaryShiftRight(n *ExprBinaryShiftRight) 147 | ExprBinarySmaller(n *ExprBinarySmaller) 148 | ExprBinarySmallerOrEqual(n *ExprBinarySmallerOrEqual) 149 | ExprBinarySpaceship(n *ExprBinarySpaceship) 150 | 151 | ExprCastArray(n *ExprCastArray) 152 | ExprCastBool(n *ExprCastBool) 153 | ExprCastDouble(n *ExprCastDouble) 154 | ExprCastInt(n *ExprCastInt) 155 | ExprCastObject(n *ExprCastObject) 156 | ExprCastString(n *ExprCastString) 157 | ExprCastUnset(n *ExprCastUnset) 158 | 159 | ScalarDnumber(n *ScalarDnumber) 160 | ScalarEncapsed(n *ScalarEncapsed) 161 | ScalarEncapsedStringPart(n *ScalarEncapsedStringPart) 162 | ScalarEncapsedStringVar(n *ScalarEncapsedStringVar) 163 | ScalarEncapsedStringBrackets(n *ScalarEncapsedStringBrackets) 164 | ScalarHeredoc(n *ScalarHeredoc) 165 | ScalarLnumber(n *ScalarLnumber) 166 | ScalarMagicConstant(n *ScalarMagicConstant) 167 | ScalarString(n *ScalarString) 168 | 169 | NameName(n *Name) 170 | NameFullyQualified(n *NameFullyQualified) 171 | NameRelative(n *NameRelative) 172 | NameNamePart(n *NamePart) 173 | } 174 | -------------------------------------------------------------------------------- /pkg/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/errors" 5 | "github.com/z7zmey/php-parser/pkg/version" 6 | ) 7 | 8 | type Config struct { 9 | Version *version.Version 10 | ErrorHandlerFunc func(e *errors.Error) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/errors/error.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/z7zmey/php-parser/pkg/position" 7 | ) 8 | 9 | // Error parsing error 10 | type Error struct { 11 | Msg string 12 | Pos *position.Position 13 | } 14 | 15 | // NewError creates and returns new Error 16 | func NewError(msg string, p *position.Position) *Error { 17 | return &Error{ 18 | Msg: msg, 19 | Pos: p, 20 | } 21 | } 22 | 23 | func (e *Error) String() string { 24 | atLine := "" 25 | if e.Pos != nil { 26 | atLine = fmt.Sprintf(" at line %d", e.Pos.StartLine) 27 | } 28 | 29 | return fmt.Sprintf("%s%s", e.Msg, atLine) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/errors/error_test.go: -------------------------------------------------------------------------------- 1 | package errors_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "gotest.tools/assert" 7 | 8 | "github.com/z7zmey/php-parser/pkg/errors" 9 | "github.com/z7zmey/php-parser/pkg/position" 10 | ) 11 | 12 | func TestConstructor(t *testing.T) { 13 | pos := position.NewPosition(1, 2, 3, 4) 14 | 15 | actual := errors.NewError("message", pos) 16 | 17 | expected := &errors.Error{ 18 | Msg: "message", 19 | Pos: pos, 20 | } 21 | 22 | assert.DeepEqual(t, expected, actual) 23 | } 24 | 25 | func TestPrint(t *testing.T) { 26 | pos := position.NewPosition(1, 2, 3, 4) 27 | 28 | Error := errors.NewError("message", pos) 29 | 30 | actual := Error.String() 31 | 32 | expected := "message at line 1" 33 | 34 | assert.DeepEqual(t, expected, actual) 35 | } 36 | 37 | func TestPrintWithotPos(t *testing.T) { 38 | Error := errors.NewError("message", nil) 39 | 40 | actual := Error.String() 41 | 42 | expected := "message" 43 | 44 | assert.DeepEqual(t, expected, actual) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/parser/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A Parser for PHP written in Go 4 | 5 | Package usage example: 6 | 7 | package main 8 | 9 | import ( 10 | "log" 11 | "os" 12 | 13 | "github.com/z7zmey/php-parser/pkg/conf" 14 | "github.com/z7zmey/php-parser/pkg/errors" 15 | "github.com/z7zmey/php-parser/pkg/parser" 16 | "github.com/z7zmey/php-parser/pkg/version" 17 | "github.com/z7zmey/php-parser/pkg/visitor/dumper" 18 | ) 19 | 20 | func main() { 21 | src := []byte(`= ID(len(_ID_index)-1) { 158 | return "ID(" + strconv.FormatInt(int64(i+57346), 10) + ")" 159 | } 160 | return _ID_name[_ID_index[i]:_ID_index[i+1]] 161 | } 162 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type Version struct { 10 | Major, Minor uint64 11 | } 12 | 13 | var ( 14 | // ErrInvalidSemVer is returned if a version can not be parsed 15 | ErrInvalidSemVer = errors.New("invalid semantic version") 16 | 17 | // ErrUnsupportedVer is returned if a version out of supported range 18 | ErrUnsupportedVer = errors.New("the version is out of supported range") 19 | 20 | php5RangeStart = &Version{Major: 5} 21 | php5RangeEnd = &Version{Major: 5, Minor: 6} 22 | 23 | php7RangeStart = &Version{Major: 7} 24 | php7RangeEnd = &Version{Major: 7, Minor: 4} 25 | ) 26 | 27 | func New(v string) (*Version, error) { 28 | // Split the parts into [0]Major, [1]Minor 29 | parts := strings.SplitN(v, ".", 2) 30 | if len(parts) != 2 { 31 | return nil, ErrInvalidSemVer 32 | } 33 | 34 | var ver = new(Version) 35 | var err error 36 | 37 | ver.Major, err = strconv.ParseUint(parts[0], 10, 64) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | ver.Minor, err = strconv.ParseUint(parts[1], 10, 64) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return ver, nil 48 | } 49 | 50 | func (v *Version) Validate() error { 51 | if !v.InRange(php5RangeStart, php5RangeEnd) && !v.InRange(php7RangeStart, php7RangeEnd) { 52 | return ErrUnsupportedVer 53 | } 54 | 55 | return nil 56 | } 57 | 58 | // Less tests if one version is less than another one 59 | func (v *Version) Less(o *Version) bool { 60 | return v.Compare(o) < 0 61 | } 62 | 63 | // LessOrEqual tests if one version is less than another one or equal 64 | func (v *Version) LessOrEqual(o *Version) bool { 65 | return v.Compare(o) <= 0 66 | } 67 | 68 | // Greater tests if one version is greater than another one 69 | func (v *Version) Greater(o *Version) bool { 70 | return v.Compare(o) > 0 71 | } 72 | 73 | // GreaterOrEqual tests if one version is greater than another one or equal 74 | func (v *Version) GreaterOrEqual(o *Version) bool { 75 | return v.Compare(o) >= 0 76 | } 77 | 78 | // GreaterOrEqual tests if one version is greater than another one or equal 79 | func (v *Version) InRange(s, e *Version) bool { 80 | return v.Compare(s) >= 0 && v.Compare(e) <= 0 81 | } 82 | 83 | // Compare compares this version to another one. It returns -1, 0, or 1 if 84 | // the version smaller, equal, or larger than the other version. 85 | func (v *Version) Compare(o *Version) int { 86 | if d := compareSegment(v.Major, o.Major); d != 0 { 87 | return d 88 | } 89 | 90 | return compareSegment(v.Minor, o.Minor) 91 | } 92 | 93 | func compareSegment(v, o uint64) int { 94 | if v < o { 95 | return -1 96 | } 97 | if v > o { 98 | return 1 99 | } 100 | 101 | return 0 102 | } 103 | -------------------------------------------------------------------------------- /pkg/version/version_test.go: -------------------------------------------------------------------------------- 1 | package version_test 2 | 3 | import ( 4 | "gotest.tools/assert" 5 | "testing" 6 | 7 | "github.com/z7zmey/php-parser/pkg/version" 8 | ) 9 | 10 | func Test(t *testing.T) { 11 | ver, err := version.New("7.4") 12 | assert.NilError(t, err) 13 | 14 | assert.Equal(t, *ver, version.Version{ 15 | Major: 7, 16 | Minor: 4, 17 | }) 18 | } 19 | 20 | func TestLeadingZero(t *testing.T) { 21 | ver, err := version.New("07.04") 22 | assert.NilError(t, err) 23 | 24 | assert.Equal(t, *ver, version.Version{ 25 | Major: 7, 26 | Minor: 4, 27 | }) 28 | } 29 | 30 | func TestInRange(t *testing.T) { 31 | s, err := version.New("7.0") 32 | assert.NilError(t, err) 33 | 34 | e, err := version.New("7.4") 35 | assert.NilError(t, err) 36 | 37 | ver, err := version.New("7.0") 38 | assert.NilError(t, err) 39 | assert.Assert(t, ver.InRange(s, e)) 40 | 41 | ver, err = version.New("7.2") 42 | assert.NilError(t, err) 43 | assert.Assert(t, ver.InRange(s, e)) 44 | 45 | ver, err = version.New("7.4") 46 | assert.NilError(t, err) 47 | assert.Assert(t, ver.InRange(s, e)) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/visitor/dumper/dumper_test.go: -------------------------------------------------------------------------------- 1 | package dumper_test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/z7zmey/php-parser/pkg/position" 6 | "github.com/z7zmey/php-parser/pkg/token" 7 | "github.com/z7zmey/php-parser/pkg/visitor/dumper" 8 | "testing" 9 | 10 | "github.com/z7zmey/php-parser/pkg/ast" 11 | ) 12 | 13 | func TestDumper_root(t *testing.T) { 14 | o := bytes.NewBufferString("") 15 | 16 | p := dumper.NewDumper(o).WithTokens().WithPositions() 17 | n := &ast.Root{ 18 | Position: &position.Position{ 19 | StartLine: 1, 20 | EndLine: 2, 21 | StartPos: 3, 22 | EndPos: 4, 23 | }, 24 | Stmts: []ast.Vertex{ 25 | &ast.StmtNop{}, 26 | }, 27 | EndTkn: &token.Token{ 28 | FreeFloating: []*token.Token{ 29 | { 30 | ID: token.T_WHITESPACE, 31 | Value: []byte(" "), 32 | Position: &position.Position{ 33 | StartLine: 1, 34 | EndLine: 2, 35 | StartPos: 3, 36 | EndPos: 4, 37 | }, 38 | }, 39 | }, 40 | }, 41 | } 42 | n.Accept(p) 43 | 44 | expected := `&ast.Root{ 45 | Position: &position.Position{ 46 | StartLine: 1, 47 | EndLine: 2, 48 | StartPos: 3, 49 | EndPos: 4, 50 | }, 51 | Stmts: []ast.Vertex{ 52 | &ast.StmtNop{ 53 | }, 54 | }, 55 | EndTkn: &token.Token{ 56 | FreeFloating: []*token.Token{ 57 | { 58 | ID: token.T_WHITESPACE, 59 | Val: []byte(" "), 60 | Position: &position.Position{ 61 | StartLine: 1, 62 | EndLine: 2, 63 | StartPos: 3, 64 | EndPos: 4, 65 | }, 66 | }, 67 | }, 68 | }, 69 | }, 70 | ` 71 | actual := o.String() 72 | 73 | if expected != actual { 74 | t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /pkg/visitor/nsresolver/namespace_resolver.go: -------------------------------------------------------------------------------- 1 | // Package visitor contains walker.visitor implementations 2 | package nsresolver 3 | 4 | import ( 5 | "errors" 6 | "github.com/z7zmey/php-parser/pkg/ast" 7 | "github.com/z7zmey/php-parser/pkg/visitor" 8 | "strings" 9 | ) 10 | 11 | // NamespaceResolver visitor 12 | type NamespaceResolver struct { 13 | visitor.Null 14 | Namespace *Namespace 15 | ResolvedNames map[ast.Vertex]string 16 | 17 | goDeep bool 18 | } 19 | 20 | // NewNamespaceResolver NamespaceResolver type constructor 21 | func NewNamespaceResolver() *NamespaceResolver { 22 | return &NamespaceResolver{ 23 | Namespace: NewNamespace(""), 24 | ResolvedNames: map[ast.Vertex]string{}, 25 | goDeep: true, 26 | } 27 | } 28 | 29 | func (nsr *NamespaceResolver) EnterNode(n ast.Vertex) bool { 30 | n.Accept(nsr) 31 | 32 | if !nsr.goDeep { 33 | nsr.goDeep = true 34 | return false 35 | } 36 | 37 | return true 38 | } 39 | 40 | func (nsr *NamespaceResolver) StmtNamespace(n *ast.StmtNamespace) { 41 | if n.Name == nil { 42 | nsr.Namespace = NewNamespace("") 43 | } else { 44 | NSParts := n.Name.(*ast.Name).Parts 45 | nsr.Namespace = NewNamespace(concatNameParts(NSParts)) 46 | } 47 | } 48 | 49 | func (nsr *NamespaceResolver) StmtUse(n *ast.StmtUseList) { 50 | useType := "" 51 | if n.Type != nil { 52 | useType = string(n.Type.(*ast.Identifier).Value) 53 | } 54 | 55 | for _, nn := range n.Uses { 56 | nsr.AddAlias(useType, nn, nil) 57 | } 58 | 59 | nsr.goDeep = false 60 | } 61 | 62 | func (nsr *NamespaceResolver) StmtGroupUse(n *ast.StmtGroupUseList) { 63 | useType := "" 64 | if n.Type != nil { 65 | useType = string(n.Type.(*ast.Identifier).Value) 66 | } 67 | 68 | for _, nn := range n.Uses { 69 | nsr.AddAlias(useType, nn, n.Prefix.(*ast.Name).Parts) 70 | } 71 | 72 | nsr.goDeep = false 73 | } 74 | 75 | func (nsr *NamespaceResolver) StmtClass(n *ast.StmtClass) { 76 | if n.Extends != nil { 77 | nsr.ResolveName(n.Extends, "") 78 | } 79 | 80 | if n.Implements != nil { 81 | for _, interfaceName := range n.Implements { 82 | nsr.ResolveName(interfaceName, "") 83 | } 84 | } 85 | 86 | if n.Name != nil { 87 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 88 | } 89 | } 90 | 91 | func (nsr *NamespaceResolver) StmtInterface(n *ast.StmtInterface) { 92 | if n.Extends != nil { 93 | for _, interfaceName := range n.Extends { 94 | nsr.ResolveName(interfaceName, "") 95 | } 96 | } 97 | 98 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 99 | } 100 | 101 | func (nsr *NamespaceResolver) StmtTrait(n *ast.StmtTrait) { 102 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 103 | } 104 | 105 | func (nsr *NamespaceResolver) StmtFunction(n *ast.StmtFunction) { 106 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value)) 107 | 108 | for _, parameter := range n.Params { 109 | nsr.ResolveType(parameter.(*ast.Parameter).Type) 110 | } 111 | 112 | if n.ReturnType != nil { 113 | nsr.ResolveType(n.ReturnType) 114 | } 115 | } 116 | 117 | func (nsr *NamespaceResolver) StmtClassMethod(n *ast.StmtClassMethod) { 118 | for _, parameter := range n.Params { 119 | nsr.ResolveType(parameter.(*ast.Parameter).Type) 120 | } 121 | 122 | if n.ReturnType != nil { 123 | nsr.ResolveType(n.ReturnType) 124 | } 125 | } 126 | 127 | func (nsr *NamespaceResolver) ExprClosure(n *ast.ExprClosure) { 128 | for _, parameter := range n.Params { 129 | nsr.ResolveType(parameter.(*ast.Parameter).Type) 130 | } 131 | 132 | if n.ReturnType != nil { 133 | nsr.ResolveType(n.ReturnType) 134 | } 135 | } 136 | 137 | func (nsr *NamespaceResolver) StmtPropertyList(n *ast.StmtPropertyList) { 138 | if n.Type != nil { 139 | nsr.ResolveType(n.Type) 140 | } 141 | } 142 | 143 | func (nsr *NamespaceResolver) StmtConstList(n *ast.StmtConstList) { 144 | for _, constant := range n.Consts { 145 | nsr.AddNamespacedName(constant, string(constant.(*ast.StmtConstant).Name.(*ast.Identifier).Value)) 146 | } 147 | } 148 | 149 | func (nsr *NamespaceResolver) ExprStaticCall(n *ast.ExprStaticCall) { 150 | nsr.ResolveName(n.Class, "") 151 | } 152 | 153 | func (nsr *NamespaceResolver) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) { 154 | nsr.ResolveName(n.Class, "") 155 | } 156 | 157 | func (nsr *NamespaceResolver) ExprClassConstFetch(n *ast.ExprClassConstFetch) { 158 | nsr.ResolveName(n.Class, "") 159 | } 160 | 161 | func (nsr *NamespaceResolver) ExprNew(n *ast.ExprNew) { 162 | nsr.ResolveName(n.Class, "") 163 | } 164 | 165 | func (nsr *NamespaceResolver) ExprInstanceOf(n *ast.ExprInstanceOf) { 166 | nsr.ResolveName(n.Class, "") 167 | } 168 | 169 | func (nsr *NamespaceResolver) StmtCatch(n *ast.StmtCatch) { 170 | for _, t := range n.Types { 171 | nsr.ResolveName(t, "") 172 | } 173 | } 174 | 175 | func (nsr *NamespaceResolver) ExprFunctionCall(n *ast.ExprFunctionCall) { 176 | nsr.ResolveName(n.Function, "function") 177 | } 178 | 179 | func (nsr *NamespaceResolver) ExprConstFetch(n *ast.ExprConstFetch) { 180 | nsr.ResolveName(n.Const, "const") 181 | } 182 | 183 | func (nsr *NamespaceResolver) StmtTraitUse(n *ast.StmtTraitUse) { 184 | for _, t := range n.Traits { 185 | nsr.ResolveName(t, "") 186 | } 187 | 188 | for _, a := range n.Adaptations { 189 | switch aa := a.(type) { 190 | case *ast.StmtTraitUsePrecedence: 191 | refTrait := aa.Trait 192 | if refTrait != nil { 193 | nsr.ResolveName(refTrait, "") 194 | } 195 | for _, insteadOf := range aa.Insteadof { 196 | nsr.ResolveName(insteadOf, "") 197 | } 198 | 199 | case *ast.StmtTraitUseAlias: 200 | refTrait := aa.Trait 201 | if refTrait != nil { 202 | nsr.ResolveName(refTrait, "") 203 | } 204 | } 205 | } 206 | } 207 | 208 | // LeaveNode is invoked after node process 209 | func (nsr *NamespaceResolver) LeaveNode(n ast.Vertex) { 210 | switch nn := n.(type) { 211 | case *ast.StmtNamespace: 212 | if nn.Stmts != nil { 213 | nsr.Namespace = NewNamespace("") 214 | } 215 | } 216 | } 217 | 218 | // AddAlias adds a new alias 219 | func (nsr *NamespaceResolver) AddAlias(useType string, nn ast.Vertex, prefix []ast.Vertex) { 220 | switch use := nn.(type) { 221 | case *ast.StmtUse: 222 | if use.Type != nil { 223 | useType = string(use.Type.(*ast.Identifier).Value) 224 | } 225 | 226 | useNameParts := use.Use.(*ast.Name).Parts 227 | var alias string 228 | if use.Alias == nil { 229 | alias = string(useNameParts[len(useNameParts)-1].(*ast.NamePart).Value) 230 | } else { 231 | alias = string(use.Alias.(*ast.Identifier).Value) 232 | } 233 | 234 | nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias) 235 | } 236 | } 237 | 238 | // AddNamespacedName adds namespaced name by node 239 | func (nsr *NamespaceResolver) AddNamespacedName(nn ast.Vertex, nodeName string) { 240 | if nsr.Namespace.Namespace == "" { 241 | nsr.ResolvedNames[nn] = nodeName 242 | } else { 243 | nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName 244 | } 245 | } 246 | 247 | // ResolveName adds a resolved fully qualified name by node 248 | func (nsr *NamespaceResolver) ResolveName(nameNode ast.Vertex, aliasType string) { 249 | resolved, err := nsr.Namespace.ResolveName(nameNode, aliasType) 250 | if err == nil { 251 | nsr.ResolvedNames[nameNode] = resolved 252 | } 253 | } 254 | 255 | // ResolveType adds a resolved fully qualified type name 256 | func (nsr *NamespaceResolver) ResolveType(n ast.Vertex) { 257 | switch nn := n.(type) { 258 | case *ast.Nullable: 259 | nsr.ResolveType(nn.Expr) 260 | case *ast.Name: 261 | nsr.ResolveName(n, "") 262 | case *ast.NameRelative: 263 | nsr.ResolveName(n, "") 264 | case *ast.NameFullyQualified: 265 | nsr.ResolveName(n, "") 266 | } 267 | } 268 | 269 | // Namespace context 270 | type Namespace struct { 271 | Namespace string 272 | Aliases map[string]map[string]string 273 | } 274 | 275 | // NewNamespace constructor 276 | func NewNamespace(NSName string) *Namespace { 277 | return &Namespace{ 278 | Namespace: NSName, 279 | Aliases: map[string]map[string]string{ 280 | "": {}, 281 | "const": {}, 282 | "function": {}, 283 | }, 284 | } 285 | } 286 | 287 | // AddAlias adds a new alias 288 | func (ns *Namespace) AddAlias(aliasType string, aliasName string, alias string) { 289 | aliasType = strings.ToLower(aliasType) 290 | 291 | if aliasType == "const" { 292 | ns.Aliases[aliasType][alias] = aliasName 293 | } else { 294 | ns.Aliases[aliasType][strings.ToLower(alias)] = aliasName 295 | } 296 | } 297 | 298 | // ResolveName returns a resolved fully qualified name 299 | func (ns *Namespace) ResolveName(nameNode ast.Vertex, aliasType string) (string, error) { 300 | switch n := nameNode.(type) { 301 | case *ast.NameFullyQualified: 302 | // Fully qualifid name is already resolved 303 | return concatNameParts(n.Parts), nil 304 | 305 | case *ast.NameRelative: 306 | if ns.Namespace == "" { 307 | return concatNameParts(n.Parts), nil 308 | } 309 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil 310 | 311 | case *ast.Name: 312 | if aliasType == "const" && len(n.Parts) == 1 { 313 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value)) 314 | if part == "true" || part == "false" || part == "null" { 315 | return part, nil 316 | } 317 | } 318 | 319 | if aliasType == "" && len(n.Parts) == 1 { 320 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value)) 321 | 322 | switch part { 323 | case "self": 324 | fallthrough 325 | case "static": 326 | fallthrough 327 | case "parent": 328 | fallthrough 329 | case "int": 330 | fallthrough 331 | case "float": 332 | fallthrough 333 | case "bool": 334 | fallthrough 335 | case "string": 336 | fallthrough 337 | case "void": 338 | fallthrough 339 | case "iterable": 340 | fallthrough 341 | case "object": 342 | return part, nil 343 | } 344 | } 345 | 346 | aliasName, err := ns.ResolveAlias(nameNode, aliasType) 347 | if err != nil { 348 | // resolve as relative name if alias not found 349 | if ns.Namespace == "" { 350 | return concatNameParts(n.Parts), nil 351 | } 352 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil 353 | } 354 | 355 | if len(n.Parts) > 1 { 356 | // if name qualified, replace first part by alias 357 | return aliasName + "\\" + concatNameParts(n.Parts[1:]), nil 358 | } 359 | 360 | return aliasName, nil 361 | } 362 | 363 | return "", errors.New("must be instance of name.Names") 364 | } 365 | 366 | // ResolveAlias returns alias or error if not found 367 | func (ns *Namespace) ResolveAlias(nameNode ast.Vertex, aliasType string) (string, error) { 368 | aliasType = strings.ToLower(aliasType) 369 | nameParts := nameNode.(*ast.Name).Parts 370 | 371 | firstPartStr := string(nameParts[0].(*ast.NamePart).Value) 372 | 373 | if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias type 374 | firstPartStr = strings.ToLower(firstPartStr) 375 | aliasType = "" 376 | } else { 377 | if aliasType != "const" { // constants are case-sensitive 378 | firstPartStr = strings.ToLower(firstPartStr) 379 | } 380 | } 381 | 382 | aliasName, ok := ns.Aliases[aliasType][firstPartStr] 383 | if !ok { 384 | return "", errors.New("Not found") 385 | } 386 | 387 | return aliasName, nil 388 | } 389 | 390 | func concatNameParts(parts ...[]ast.Vertex) string { 391 | str := "" 392 | 393 | for _, p := range parts { 394 | for _, n := range p { 395 | if str == "" { 396 | str = string(n.(*ast.NamePart).Value) 397 | } else { 398 | str = str + "\\" + string(n.(*ast.NamePart).Value) 399 | } 400 | } 401 | } 402 | 403 | return str 404 | } 405 | -------------------------------------------------------------------------------- /pkg/visitor/nsresolver/namespace_resolver_test.go: -------------------------------------------------------------------------------- 1 | package nsresolver_test 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/visitor/nsresolver" 5 | "github.com/z7zmey/php-parser/pkg/visitor/traverser" 6 | "testing" 7 | 8 | "gotest.tools/assert" 9 | 10 | "github.com/z7zmey/php-parser/pkg/ast" 11 | ) 12 | 13 | func TestResolveStaticCall(t *testing.T) { 14 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 15 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 16 | 17 | stxTree := &ast.StmtStmtList{ 18 | Stmts: []ast.Vertex{ 19 | &ast.StmtUseList{ 20 | Uses: []ast.Vertex{ 21 | &ast.StmtUse{ 22 | Use: nameAB, 23 | }, 24 | }, 25 | }, 26 | &ast.ExprStaticCall{ 27 | Class: nameBC, 28 | Call: &ast.Identifier{Value: []byte("foo")}, 29 | }, 30 | }, 31 | } 32 | 33 | expected := map[ast.Vertex]string{ 34 | nameBC: "A\\B\\C", 35 | } 36 | 37 | nsResolver := nsresolver.NewNamespaceResolver() 38 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 39 | 40 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 41 | } 42 | 43 | func TestResolveStaticPropertyFetch(t *testing.T) { 44 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 45 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 46 | 47 | stxTree := &ast.StmtStmtList{ 48 | Stmts: []ast.Vertex{ 49 | &ast.StmtUseList{ 50 | Uses: []ast.Vertex{ 51 | &ast.StmtUse{ 52 | Use: nameAB, 53 | }, 54 | }, 55 | }, 56 | &ast.ExprStaticPropertyFetch{ 57 | Class: nameBC, 58 | Prop: &ast.Identifier{Value: []byte("foo")}, 59 | }, 60 | }, 61 | } 62 | 63 | expected := map[ast.Vertex]string{ 64 | nameBC: "A\\B\\C", 65 | } 66 | 67 | nsResolver := nsresolver.NewNamespaceResolver() 68 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 69 | 70 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 71 | } 72 | 73 | func TestResolveClassConstFetch(t *testing.T) { 74 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 75 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 76 | 77 | stxTree := &ast.StmtStmtList{ 78 | Stmts: []ast.Vertex{ 79 | &ast.StmtUseList{ 80 | Uses: []ast.Vertex{ 81 | &ast.StmtUse{ 82 | Use: nameAB, 83 | }, 84 | }, 85 | }, 86 | &ast.ExprClassConstFetch{ 87 | Class: nameBC, 88 | Const: &ast.Identifier{Value: []byte("FOO")}, 89 | }, 90 | }, 91 | } 92 | 93 | expected := map[ast.Vertex]string{ 94 | nameBC: "A\\B\\C", 95 | } 96 | 97 | nsResolver := nsresolver.NewNamespaceResolver() 98 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 99 | 100 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 101 | } 102 | 103 | func TestResolveNew(t *testing.T) { 104 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 105 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 106 | 107 | stxTree := &ast.StmtStmtList{ 108 | Stmts: []ast.Vertex{ 109 | &ast.StmtUseList{ 110 | Uses: []ast.Vertex{ 111 | &ast.StmtUse{ 112 | Use: nameAB, 113 | }, 114 | }, 115 | }, 116 | &ast.ExprNew{ 117 | Class: nameBC, 118 | }, 119 | }, 120 | } 121 | 122 | expected := map[ast.Vertex]string{ 123 | nameBC: "A\\B\\C", 124 | } 125 | 126 | nsResolver := nsresolver.NewNamespaceResolver() 127 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 128 | 129 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 130 | } 131 | 132 | func TestResolveInstanceOf(t *testing.T) { 133 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 134 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 135 | 136 | stxTree := &ast.StmtStmtList{ 137 | Stmts: []ast.Vertex{ 138 | &ast.StmtUseList{ 139 | Uses: []ast.Vertex{ 140 | &ast.StmtUse{ 141 | Use: nameAB, 142 | }, 143 | }, 144 | }, 145 | &ast.ExprInstanceOf{ 146 | Expr: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 147 | Class: nameBC, 148 | }, 149 | }, 150 | } 151 | 152 | expected := map[ast.Vertex]string{ 153 | nameBC: "A\\B\\C", 154 | } 155 | 156 | nsResolver := nsresolver.NewNamespaceResolver() 157 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 158 | 159 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 160 | } 161 | 162 | func TestResolveInstanceCatch(t *testing.T) { 163 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 164 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 165 | 166 | nameDE := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("D")}, &ast.NamePart{Value: []byte("E")}}} 167 | nameF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}}} 168 | 169 | stxTree := &ast.StmtStmtList{ 170 | Stmts: []ast.Vertex{ 171 | &ast.StmtUseList{ 172 | Uses: []ast.Vertex{ 173 | &ast.StmtUse{ 174 | Use: nameAB, 175 | }, 176 | &ast.StmtUse{ 177 | Use: nameDE, 178 | Alias: &ast.Identifier{Value: []byte("F")}, 179 | }, 180 | }, 181 | }, 182 | &ast.StmtTry{ 183 | Stmts: []ast.Vertex{}, 184 | Catches: []ast.Vertex{ 185 | &ast.StmtCatch{ 186 | Types: []ast.Vertex{ 187 | nameBC, 188 | nameF, 189 | }, 190 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 191 | Stmts: []ast.Vertex{}, 192 | }, 193 | }, 194 | }, 195 | }, 196 | } 197 | 198 | expected := map[ast.Vertex]string{ 199 | nameBC: "A\\B\\C", 200 | nameF: "D\\E", 201 | } 202 | 203 | nsResolver := nsresolver.NewNamespaceResolver() 204 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 205 | 206 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 207 | } 208 | 209 | func TestResolveFunctionCall(t *testing.T) { 210 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 211 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}} 212 | 213 | stxTree := &ast.StmtStmtList{ 214 | Stmts: []ast.Vertex{ 215 | &ast.StmtUseList{ 216 | Type: &ast.Identifier{Value: []byte("function")}, 217 | Uses: []ast.Vertex{ 218 | &ast.StmtUse{ 219 | Use: nameAB, 220 | }, 221 | }, 222 | }, 223 | &ast.ExprFunctionCall{ 224 | Function: nameB, 225 | }, 226 | }, 227 | } 228 | 229 | expected := map[ast.Vertex]string{ 230 | nameB: "A\\B", 231 | } 232 | 233 | nsResolver := nsresolver.NewNamespaceResolver() 234 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 235 | 236 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 237 | } 238 | 239 | func TestResolveConstFetch(t *testing.T) { 240 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 241 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}} 242 | 243 | stxTree := &ast.StmtStmtList{ 244 | Stmts: []ast.Vertex{ 245 | &ast.StmtUseList{ 246 | Type: &ast.Identifier{Value: []byte("const")}, 247 | Uses: []ast.Vertex{ 248 | &ast.StmtUse{ 249 | Use: nameAB, 250 | }, 251 | }, 252 | }, 253 | &ast.ExprConstFetch{ 254 | Const: nameB, 255 | }, 256 | }, 257 | } 258 | 259 | expected := map[ast.Vertex]string{ 260 | nameB: "A\\B", 261 | } 262 | 263 | nsResolver := nsresolver.NewNamespaceResolver() 264 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 265 | 266 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 267 | } 268 | 269 | func TestResolveGroupUse(t *testing.T) { 270 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 271 | nameBD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("D")}}} 272 | nameE := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("E")}}} 273 | nameC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}}} 274 | nameF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}}} 275 | 276 | stxTree := &ast.StmtStmtList{ 277 | Stmts: []ast.Vertex{ 278 | &ast.StmtGroupUseList{ 279 | Prefix: nameAB, 280 | Uses: []ast.Vertex{ 281 | &ast.StmtUse{ 282 | Type: &ast.Identifier{Value: []byte("Function")}, 283 | Use: nameF, 284 | }, 285 | &ast.StmtUse{ 286 | Type: &ast.Identifier{Value: []byte("const")}, 287 | Use: nameC, 288 | }, 289 | }, 290 | }, 291 | &ast.StmtGroupUseList{ 292 | Prefix: nameBD, 293 | Type: &ast.Identifier{Value: []byte("Function")}, 294 | Uses: []ast.Vertex{ 295 | &ast.StmtUse{ 296 | Use: nameE, 297 | }, 298 | }, 299 | }, 300 | &ast.ExprConstFetch{ 301 | Const: nameC, 302 | }, 303 | &ast.ExprFunctionCall{ 304 | Function: nameF, 305 | }, 306 | &ast.ExprFunctionCall{ 307 | Function: nameE, 308 | }, 309 | }, 310 | } 311 | 312 | expected := map[ast.Vertex]string{ 313 | nameC: "A\\B\\C", 314 | nameF: "A\\B\\F", 315 | nameE: "B\\D\\E", 316 | } 317 | 318 | nsResolver := nsresolver.NewNamespaceResolver() 319 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 320 | 321 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 322 | } 323 | 324 | func TestResolveTraitUse(t *testing.T) { 325 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 326 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}} 327 | nameD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("D")}}} 328 | 329 | fullyQualifiedNameB := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}} 330 | fullyQualifiedNameBC := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 331 | relativeNameB := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}} 332 | relativeNameBC := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 333 | 334 | stxTree := &ast.StmtStmtList{ 335 | Stmts: []ast.Vertex{ 336 | &ast.StmtUseList{ 337 | Uses: []ast.Vertex{ 338 | &ast.StmtUse{ 339 | Use: nameAB, 340 | }, 341 | }, 342 | }, 343 | &ast.StmtTraitUse{ 344 | Traits: []ast.Vertex{ 345 | nameB, 346 | relativeNameB, 347 | }, 348 | Adaptations: []ast.Vertex{ 349 | &ast.StmtTraitUsePrecedence{ 350 | Trait: fullyQualifiedNameB, 351 | Method: &ast.Identifier{Value: []byte("foo")}, 352 | Insteadof: []ast.Vertex{fullyQualifiedNameBC}, 353 | }, 354 | &ast.StmtTraitUseAlias{ 355 | Trait: relativeNameBC, 356 | Method: &ast.Identifier{Value: []byte("foo")}, 357 | Alias: &ast.Identifier{Value: []byte("bar")}, 358 | }, 359 | }, 360 | }, 361 | &ast.StmtTraitUse{ 362 | Traits: []ast.Vertex{ 363 | nameD, 364 | }, 365 | }, 366 | }, 367 | } 368 | 369 | expected := map[ast.Vertex]string{ 370 | nameB: "A\\B", 371 | nameD: "D", 372 | relativeNameB: "B", 373 | fullyQualifiedNameB: "B", 374 | fullyQualifiedNameBC: "B\\C", 375 | relativeNameBC: "B\\C", 376 | } 377 | 378 | nsResolver := nsresolver.NewNamespaceResolver() 379 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 380 | 381 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 382 | } 383 | 384 | func TestResolveClassName(t *testing.T) { 385 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 386 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 387 | 388 | class := &ast.StmtClass{ 389 | Name: &ast.Identifier{Value: []byte("A")}, 390 | Extends: nameAB, 391 | Implements: []ast.Vertex{ 392 | nameBC, 393 | }, 394 | } 395 | 396 | stxTree := &ast.StmtStmtList{ 397 | Stmts: []ast.Vertex{ 398 | class, 399 | }, 400 | } 401 | 402 | expected := map[ast.Vertex]string{ 403 | class: "A", 404 | nameAB: "A\\B", 405 | nameBC: "B\\C", 406 | } 407 | 408 | nsResolver := nsresolver.NewNamespaceResolver() 409 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 410 | 411 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 412 | } 413 | 414 | func TestResolveInterfaceName(t *testing.T) { 415 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 416 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 417 | 418 | interfaceNode := &ast.StmtInterface{ 419 | Name: &ast.Identifier{Value: []byte("A")}, 420 | Extends: []ast.Vertex{ 421 | nameAB, 422 | nameBC, 423 | }, 424 | } 425 | 426 | stxTree := &ast.StmtStmtList{ 427 | Stmts: []ast.Vertex{ 428 | interfaceNode, 429 | }, 430 | } 431 | 432 | expected := map[ast.Vertex]string{ 433 | interfaceNode: "A", 434 | nameAB: "A\\B", 435 | nameBC: "B\\C", 436 | } 437 | 438 | nsResolver := nsresolver.NewNamespaceResolver() 439 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 440 | 441 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 442 | } 443 | 444 | func TestResolveTraitName(t *testing.T) { 445 | traitNode := &ast.StmtTrait{ 446 | Name: &ast.Identifier{Value: []byte("A")}, 447 | Stmts: []ast.Vertex{}, 448 | } 449 | 450 | stxTree := &ast.StmtStmtList{ 451 | Stmts: []ast.Vertex{ 452 | traitNode, 453 | }, 454 | } 455 | 456 | expected := map[ast.Vertex]string{ 457 | traitNode: "A", 458 | } 459 | 460 | nsResolver := nsresolver.NewNamespaceResolver() 461 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 462 | 463 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 464 | } 465 | 466 | func TestResolveFunctionName(t *testing.T) { 467 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 468 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 469 | 470 | functionNode := &ast.StmtFunction{ 471 | Name: &ast.Identifier{Value: []byte("A")}, 472 | Params: []ast.Vertex{ 473 | &ast.Parameter{ 474 | Type: nameAB, 475 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 476 | }, 477 | }, 478 | ReturnType: &ast.Nullable{Expr: nameBC}, 479 | Stmts: []ast.Vertex{}, 480 | } 481 | 482 | stxTree := &ast.StmtStmtList{ 483 | Stmts: []ast.Vertex{ 484 | functionNode, 485 | }, 486 | } 487 | 488 | expected := map[ast.Vertex]string{ 489 | functionNode: "A", 490 | nameAB: "A\\B", 491 | nameBC: "B\\C", 492 | } 493 | 494 | nsResolver := nsresolver.NewNamespaceResolver() 495 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 496 | 497 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 498 | } 499 | 500 | func TestResolveMethodName(t *testing.T) { 501 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 502 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 503 | 504 | methodNode := &ast.StmtClassMethod{ 505 | Name: &ast.Identifier{Value: []byte("A")}, 506 | Params: []ast.Vertex{ 507 | &ast.Parameter{ 508 | Type: nameAB, 509 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 510 | }, 511 | }, 512 | ReturnType: &ast.Nullable{Expr: nameBC}, 513 | Stmt: &ast.StmtStmtList{ 514 | Stmts: []ast.Vertex{}, 515 | }, 516 | } 517 | 518 | expected := map[ast.Vertex]string{ 519 | nameAB: "A\\B", 520 | nameBC: "B\\C", 521 | } 522 | 523 | nsResolver := nsresolver.NewNamespaceResolver() 524 | traverser.NewTraverser(nsResolver).Traverse(methodNode) 525 | 526 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 527 | } 528 | 529 | func TestResolveClosureName(t *testing.T) { 530 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 531 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}} 532 | 533 | closureNode := &ast.ExprClosure{ 534 | Params: []ast.Vertex{ 535 | &ast.Parameter{ 536 | Type: nameAB, 537 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 538 | }, 539 | }, 540 | ReturnType: &ast.Nullable{Expr: nameBC}, 541 | Stmts: []ast.Vertex{}, 542 | } 543 | 544 | expected := map[ast.Vertex]string{ 545 | nameAB: "A\\B", 546 | nameBC: "B\\C", 547 | } 548 | 549 | nsResolver := nsresolver.NewNamespaceResolver() 550 | traverser.NewTraverser(nsResolver).Traverse(closureNode) 551 | 552 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 553 | } 554 | 555 | func TestResolveConstantsName(t *testing.T) { 556 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 557 | 558 | constantB := &ast.StmtConstant{ 559 | Name: &ast.Identifier{Value: []byte("B")}, 560 | Expr: &ast.ScalarLnumber{Value: []byte("1")}, 561 | } 562 | constantC := &ast.StmtConstant{ 563 | Name: &ast.Identifier{Value: []byte("C")}, 564 | Expr: &ast.ScalarLnumber{Value: []byte("1")}, 565 | } 566 | 567 | stxTree := &ast.StmtStmtList{ 568 | Stmts: []ast.Vertex{ 569 | &ast.StmtNamespace{ 570 | Name: nameAB, 571 | }, 572 | &ast.StmtConstList{ 573 | Consts: []ast.Vertex{ 574 | constantB, 575 | constantC, 576 | }, 577 | }, 578 | }, 579 | } 580 | 581 | expected := map[ast.Vertex]string{ 582 | constantB: "A\\B\\B", 583 | constantC: "A\\B\\C", 584 | } 585 | 586 | nsResolver := nsresolver.NewNamespaceResolver() 587 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 588 | 589 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 590 | } 591 | 592 | func TestResolveNamespaces(t *testing.T) { 593 | namespaceAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 594 | namespaceCD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("D")}}} 595 | 596 | nameAC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("C")}}} 597 | nameCF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("F")}}} 598 | nameFG := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}, &ast.NamePart{Value: []byte("G")}}} 599 | relativeNameCE := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("E")}}} 600 | 601 | constantB := &ast.StmtConstant{ 602 | Name: &ast.Identifier{Value: []byte("B")}, 603 | Expr: &ast.ScalarLnumber{Value: []byte("1")}, 604 | } 605 | constantC := &ast.StmtConstant{ 606 | Name: &ast.Identifier{Value: []byte("C")}, 607 | Expr: &ast.ScalarLnumber{Value: []byte("1")}, 608 | } 609 | 610 | stxTree := &ast.StmtStmtList{ 611 | Stmts: []ast.Vertex{ 612 | &ast.StmtNamespace{ 613 | Name: namespaceAB, 614 | }, 615 | &ast.StmtConstList{ 616 | Consts: []ast.Vertex{ 617 | constantB, 618 | constantC, 619 | }, 620 | }, 621 | &ast.ExprStaticCall{ 622 | Class: nameFG, 623 | Call: &ast.Identifier{Value: []byte("foo")}, 624 | }, 625 | &ast.StmtNamespace{ 626 | Stmts: []ast.Vertex{}, 627 | }, 628 | &ast.StmtNamespace{ 629 | Name: namespaceCD, 630 | Stmts: []ast.Vertex{ 631 | &ast.StmtUseList{ 632 | Uses: []ast.Vertex{ 633 | &ast.StmtUse{ 634 | Use: nameAC, 635 | }, 636 | }, 637 | }, 638 | &ast.ExprStaticCall{ 639 | Class: relativeNameCE, 640 | Call: &ast.Identifier{Value: []byte("foo")}, 641 | }, 642 | &ast.ExprStaticCall{ 643 | Class: nameCF, 644 | Call: &ast.Identifier{Value: []byte("foo")}, 645 | }, 646 | }, 647 | }, 648 | }, 649 | } 650 | 651 | expected := map[ast.Vertex]string{ 652 | constantB: "A\\B\\B", 653 | constantC: "A\\B\\C", 654 | nameFG: "A\\B\\F\\G", 655 | relativeNameCE: "C\\D\\C\\E", 656 | nameCF: "A\\C\\F", 657 | } 658 | 659 | nsResolver := nsresolver.NewNamespaceResolver() 660 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 661 | 662 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 663 | } 664 | 665 | func TestResolveStaticCallDinamicClassName(t *testing.T) { 666 | stxTree := &ast.StmtStmtList{ 667 | Stmts: []ast.Vertex{ 668 | &ast.ExprStaticCall{ 669 | Class: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}}, 670 | Call: &ast.Identifier{Value: []byte("foo")}, 671 | }, 672 | }, 673 | } 674 | 675 | expected := map[ast.Vertex]string{} 676 | 677 | nsResolver := nsresolver.NewNamespaceResolver() 678 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 679 | 680 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 681 | } 682 | 683 | func TestDoNotResolveReservedConstants(t *testing.T) { 684 | namespaceName := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("Foo")}}} 685 | 686 | constantTrue := &ast.Name{ 687 | Parts: []ast.Vertex{ 688 | &ast.NamePart{Value: []byte("True")}, 689 | }, 690 | } 691 | 692 | constantFalse := &ast.Name{ 693 | Parts: []ast.Vertex{ 694 | &ast.NamePart{Value: []byte("False")}, 695 | }, 696 | } 697 | 698 | constantNull := &ast.Name{ 699 | Parts: []ast.Vertex{ 700 | &ast.NamePart{Value: []byte("NULL")}, 701 | }, 702 | } 703 | 704 | stxTree := &ast.StmtStmtList{ 705 | Stmts: []ast.Vertex{ 706 | &ast.StmtNamespace{ 707 | Name: namespaceName, 708 | }, 709 | &ast.StmtExpression{ 710 | Expr: &ast.ExprConstFetch{ 711 | Const: constantTrue, 712 | }, 713 | }, 714 | &ast.StmtExpression{ 715 | Expr: &ast.ExprConstFetch{ 716 | Const: constantFalse, 717 | }, 718 | }, 719 | &ast.StmtExpression{ 720 | Expr: &ast.ExprConstFetch{ 721 | Const: constantNull, 722 | }, 723 | }, 724 | }, 725 | } 726 | 727 | expected := map[ast.Vertex]string{ 728 | constantTrue: "true", 729 | constantFalse: "false", 730 | constantNull: "null", 731 | } 732 | 733 | nsResolver := nsresolver.NewNamespaceResolver() 734 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 735 | 736 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 737 | } 738 | 739 | func TestDoNotResolveReservedNames(t *testing.T) { 740 | 741 | nameInt := &ast.Name{ 742 | Parts: []ast.Vertex{ 743 | &ast.NamePart{Value: []byte("int")}, 744 | }, 745 | } 746 | 747 | nameFloat := &ast.Name{ 748 | Parts: []ast.Vertex{ 749 | &ast.NamePart{Value: []byte("float")}, 750 | }, 751 | } 752 | 753 | nameBool := &ast.Name{ 754 | Parts: []ast.Vertex{ 755 | &ast.NamePart{Value: []byte("bool")}, 756 | }, 757 | } 758 | 759 | nameString := &ast.Name{ 760 | Parts: []ast.Vertex{ 761 | &ast.NamePart{Value: []byte("string")}, 762 | }, 763 | } 764 | 765 | nameVoid := &ast.Name{ 766 | Parts: []ast.Vertex{ 767 | &ast.NamePart{Value: []byte("void")}, 768 | }, 769 | } 770 | 771 | nameIterable := &ast.Name{ 772 | Parts: []ast.Vertex{ 773 | &ast.NamePart{Value: []byte("iterable")}, 774 | }, 775 | } 776 | 777 | nameObject := &ast.Name{ 778 | Parts: []ast.Vertex{ 779 | &ast.NamePart{Value: []byte("object")}, 780 | }, 781 | } 782 | 783 | function := &ast.StmtFunction{ 784 | Name: &ast.Identifier{Value: []byte("bar")}, 785 | Params: []ast.Vertex{ 786 | &ast.Parameter{ 787 | Type: nameInt, 788 | Var: &ast.ExprVariable{ 789 | Name: &ast.Identifier{Value: []byte("Int")}, 790 | }, 791 | }, 792 | &ast.Parameter{ 793 | Type: nameFloat, 794 | Var: &ast.ExprVariable{ 795 | Name: &ast.Identifier{Value: []byte("Float")}, 796 | }, 797 | }, 798 | &ast.Parameter{ 799 | Type: nameBool, 800 | Var: &ast.ExprVariable{ 801 | Name: &ast.Identifier{Value: []byte("Bool")}, 802 | }, 803 | }, 804 | &ast.Parameter{ 805 | Type: nameString, 806 | Var: &ast.ExprVariable{ 807 | Name: &ast.Identifier{Value: []byte("String")}, 808 | }, 809 | }, 810 | &ast.Parameter{ 811 | Type: nameVoid, 812 | Var: &ast.ExprVariable{ 813 | Name: &ast.Identifier{Value: []byte("Void")}, 814 | }, 815 | }, 816 | &ast.Parameter{ 817 | Type: nameIterable, 818 | Var: &ast.ExprVariable{ 819 | Name: &ast.Identifier{Value: []byte("Iterable")}, 820 | }, 821 | }, 822 | &ast.Parameter{ 823 | Type: nameObject, 824 | Var: &ast.ExprVariable{ 825 | Name: &ast.Identifier{Value: []byte("Object")}, 826 | }, 827 | }, 828 | }, 829 | } 830 | 831 | stxTree := &ast.StmtStmtList{ 832 | Stmts: []ast.Vertex{ 833 | &ast.StmtNamespace{ 834 | Name: &ast.Name{ 835 | Parts: []ast.Vertex{ 836 | &ast.NamePart{Value: []byte("Foo")}, 837 | }, 838 | }, 839 | }, 840 | function, 841 | }, 842 | } 843 | 844 | expected := map[ast.Vertex]string{ 845 | function: "Foo\\bar", 846 | nameInt: "int", 847 | nameFloat: "float", 848 | nameBool: "bool", 849 | nameString: "string", 850 | nameVoid: "void", 851 | nameIterable: "iterable", 852 | nameObject: "object", 853 | } 854 | 855 | nsResolver := nsresolver.NewNamespaceResolver() 856 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 857 | 858 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 859 | } 860 | 861 | func TestDoNotResolveReservedSpecialNames(t *testing.T) { 862 | 863 | nameSelf := &ast.Name{ 864 | Parts: []ast.Vertex{ 865 | &ast.NamePart{Value: []byte("Self")}, 866 | }, 867 | } 868 | 869 | nameStatic := &ast.Name{ 870 | Parts: []ast.Vertex{ 871 | &ast.NamePart{Value: []byte("Static")}, 872 | }, 873 | } 874 | 875 | nameParent := &ast.Name{ 876 | Parts: []ast.Vertex{ 877 | &ast.NamePart{Value: []byte("Parent")}, 878 | }, 879 | } 880 | 881 | cls := &ast.StmtClass{ 882 | Name: &ast.Identifier{Value: []byte("Bar")}, 883 | Stmts: []ast.Vertex{ 884 | &ast.StmtExpression{ 885 | Expr: &ast.ExprStaticCall{ 886 | Class: nameSelf, 887 | Call: &ast.Identifier{Value: []byte("func")}, 888 | }, 889 | }, 890 | &ast.StmtExpression{ 891 | Expr: &ast.ExprStaticCall{ 892 | Class: nameStatic, 893 | Call: &ast.Identifier{Value: []byte("func")}, 894 | }, 895 | }, 896 | &ast.StmtExpression{ 897 | Expr: &ast.ExprStaticCall{ 898 | Class: nameParent, 899 | Call: &ast.Identifier{Value: []byte("func")}, 900 | }, 901 | }, 902 | }, 903 | } 904 | 905 | stxTree := &ast.StmtStmtList{ 906 | Stmts: []ast.Vertex{ 907 | &ast.StmtNamespace{ 908 | Name: &ast.Name{ 909 | Parts: []ast.Vertex{ 910 | &ast.NamePart{Value: []byte("Foo")}, 911 | }, 912 | }, 913 | }, 914 | cls, 915 | }, 916 | } 917 | 918 | expected := map[ast.Vertex]string{ 919 | cls: "Foo\\Bar", 920 | nameSelf: "self", 921 | nameStatic: "static", 922 | nameParent: "parent", 923 | } 924 | 925 | nsResolver := nsresolver.NewNamespaceResolver() 926 | traverser.NewTraverser(nsResolver).Traverse(stxTree) 927 | 928 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 929 | } 930 | func TestResolvePropertyTypeName(t *testing.T) { 931 | nameSimple := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 932 | nameRelative := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 933 | nameFullyQualified := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}} 934 | 935 | propertyNodeSimple := &ast.StmtPropertyList{ 936 | Type: nameSimple, 937 | } 938 | 939 | propertyNodeRelative := &ast.StmtPropertyList{ 940 | Type: nameRelative, 941 | } 942 | 943 | propertyNodeFullyQualified := &ast.StmtPropertyList{ 944 | Type: nameFullyQualified, 945 | } 946 | 947 | classNode := &ast.StmtClass{ 948 | Name: &ast.Identifier{Value: []byte("Bar")}, 949 | Stmts: []ast.Vertex{ 950 | propertyNodeSimple, 951 | propertyNodeRelative, 952 | propertyNodeFullyQualified, 953 | }, 954 | } 955 | 956 | stmts := &ast.StmtStmtList{ 957 | Stmts: []ast.Vertex{ 958 | &ast.StmtNamespace{ 959 | Name: &ast.Name{ 960 | Parts: []ast.Vertex{ 961 | &ast.NamePart{Value: []byte("Foo")}, 962 | }, 963 | }, 964 | }, 965 | classNode, 966 | }, 967 | } 968 | 969 | expected := map[ast.Vertex]string{ 970 | nameSimple: "Foo\\A\\B", 971 | nameRelative: "Foo\\A\\B", 972 | nameFullyQualified: "A\\B", 973 | classNode: "Foo\\Bar", 974 | } 975 | 976 | nsResolver := nsresolver.NewNamespaceResolver() 977 | traverser.NewTraverser(nsResolver).Traverse(stmts) 978 | 979 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames) 980 | } 981 | -------------------------------------------------------------------------------- /pkg/visitor/null.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/ast" 5 | ) 6 | 7 | type Null struct { 8 | } 9 | 10 | func (v *Null) Enter(_ string, _ bool) { 11 | // do nothing 12 | } 13 | func (v *Null) Leave(_ string, _ bool) { 14 | // do nothing 15 | } 16 | 17 | func (v *Null) EnterNode(_ ast.Vertex) bool { 18 | return true 19 | } 20 | 21 | func (v *Null) LeaveNode(_ ast.Vertex) { 22 | // do nothing 23 | } 24 | 25 | func (v *Null) Root(_ *ast.Root) { 26 | // do nothing 27 | } 28 | 29 | func (v *Null) Nullable(_ *ast.Nullable) { 30 | // do nothing 31 | } 32 | 33 | func (v *Null) Parameter(_ *ast.Parameter) { 34 | // do nothing 35 | } 36 | 37 | func (v *Null) Identifier(_ *ast.Identifier) { 38 | // do nothing 39 | } 40 | 41 | func (v *Null) Argument(_ *ast.Argument) { 42 | // do nothing 43 | } 44 | 45 | func (v *Null) StmtBreak(_ *ast.StmtBreak) { 46 | // do nothing 47 | } 48 | 49 | func (v *Null) StmtCase(_ *ast.StmtCase) { 50 | // do nothing 51 | } 52 | 53 | func (v *Null) StmtCatch(_ *ast.StmtCatch) { 54 | // do nothing 55 | } 56 | 57 | func (v *Null) StmtClass(_ *ast.StmtClass) { 58 | // do nothing 59 | } 60 | 61 | func (v *Null) StmtClassConstList(_ *ast.StmtClassConstList) { 62 | // do nothing 63 | } 64 | 65 | func (v *Null) StmtClassMethod(_ *ast.StmtClassMethod) { 66 | // do nothing 67 | } 68 | 69 | func (v *Null) StmtConstList(_ *ast.StmtConstList) { 70 | // do nothing 71 | } 72 | 73 | func (v *Null) StmtConstant(_ *ast.StmtConstant) { 74 | // do nothing 75 | } 76 | 77 | func (v *Null) StmtContinue(_ *ast.StmtContinue) { 78 | // do nothing 79 | } 80 | 81 | func (v *Null) StmtDeclare(_ *ast.StmtDeclare) { 82 | // do nothing 83 | } 84 | 85 | func (v *Null) StmtDefault(_ *ast.StmtDefault) { 86 | // do nothing 87 | } 88 | 89 | func (v *Null) StmtDo(_ *ast.StmtDo) { 90 | // do nothing 91 | } 92 | 93 | func (v *Null) StmtEcho(_ *ast.StmtEcho) { 94 | // do nothing 95 | } 96 | 97 | func (v *Null) StmtElse(_ *ast.StmtElse) { 98 | // do nothing 99 | } 100 | 101 | func (v *Null) StmtElseIf(_ *ast.StmtElseIf) { 102 | // do nothing 103 | } 104 | 105 | func (v *Null) StmtExpression(_ *ast.StmtExpression) { 106 | // do nothing 107 | } 108 | 109 | func (v *Null) StmtFinally(_ *ast.StmtFinally) { 110 | // do nothing 111 | } 112 | 113 | func (v *Null) StmtFor(_ *ast.StmtFor) { 114 | // do nothing 115 | } 116 | 117 | func (v *Null) StmtForeach(_ *ast.StmtForeach) { 118 | // do nothing 119 | } 120 | 121 | func (v *Null) StmtFunction(_ *ast.StmtFunction) { 122 | // do nothing 123 | } 124 | 125 | func (v *Null) StmtGlobal(_ *ast.StmtGlobal) { 126 | // do nothing 127 | } 128 | 129 | func (v *Null) StmtGoto(_ *ast.StmtGoto) { 130 | // do nothing 131 | } 132 | 133 | func (v *Null) StmtHaltCompiler(_ *ast.StmtHaltCompiler) { 134 | // do nothing 135 | } 136 | 137 | func (v *Null) StmtIf(_ *ast.StmtIf) { 138 | // do nothing 139 | } 140 | 141 | func (v *Null) StmtInlineHtml(_ *ast.StmtInlineHtml) { 142 | // do nothing 143 | } 144 | 145 | func (v *Null) StmtInterface(_ *ast.StmtInterface) { 146 | // do nothing 147 | } 148 | 149 | func (v *Null) StmtLabel(_ *ast.StmtLabel) { 150 | // do nothing 151 | } 152 | 153 | func (v *Null) StmtNamespace(_ *ast.StmtNamespace) { 154 | // do nothing 155 | } 156 | 157 | func (v *Null) StmtNop(_ *ast.StmtNop) { 158 | // do nothing 159 | } 160 | 161 | func (v *Null) StmtProperty(_ *ast.StmtProperty) { 162 | // do nothing 163 | } 164 | 165 | func (v *Null) StmtPropertyList(_ *ast.StmtPropertyList) { 166 | // do nothing 167 | } 168 | 169 | func (v *Null) StmtReturn(_ *ast.StmtReturn) { 170 | // do nothing 171 | } 172 | 173 | func (v *Null) StmtStatic(_ *ast.StmtStatic) { 174 | // do nothing 175 | } 176 | 177 | func (v *Null) StmtStaticVar(_ *ast.StmtStaticVar) { 178 | // do nothing 179 | } 180 | 181 | func (v *Null) StmtStmtList(_ *ast.StmtStmtList) { 182 | // do nothing 183 | } 184 | 185 | func (v *Null) StmtSwitch(_ *ast.StmtSwitch) { 186 | // do nothing 187 | } 188 | 189 | func (v *Null) StmtThrow(_ *ast.StmtThrow) { 190 | // do nothing 191 | } 192 | 193 | func (v *Null) StmtTrait(_ *ast.StmtTrait) { 194 | // do nothing 195 | } 196 | 197 | func (v *Null) StmtTraitUse(_ *ast.StmtTraitUse) { 198 | // do nothing 199 | } 200 | 201 | func (v *Null) StmtTraitUseAlias(_ *ast.StmtTraitUseAlias) { 202 | // do nothing 203 | } 204 | 205 | func (v *Null) StmtTraitUsePrecedence(_ *ast.StmtTraitUsePrecedence) { 206 | // do nothing 207 | } 208 | 209 | func (v *Null) StmtTry(_ *ast.StmtTry) { 210 | // do nothing 211 | } 212 | 213 | func (v *Null) StmtUnset(_ *ast.StmtUnset) { 214 | // do nothing 215 | } 216 | 217 | func (v *Null) StmtUse(_ *ast.StmtUseList) { 218 | // do nothing 219 | } 220 | 221 | func (v *Null) StmtGroupUse(_ *ast.StmtGroupUseList) { 222 | // do nothing 223 | } 224 | 225 | func (v *Null) StmtUseDeclaration(_ *ast.StmtUse) { 226 | // do nothing 227 | } 228 | 229 | func (v *Null) StmtWhile(_ *ast.StmtWhile) { 230 | // do nothing 231 | } 232 | 233 | func (v *Null) ExprArray(_ *ast.ExprArray) { 234 | // do nothing 235 | } 236 | 237 | func (v *Null) ExprArrayDimFetch(_ *ast.ExprArrayDimFetch) { 238 | // do nothing 239 | } 240 | 241 | func (v *Null) ExprArrayItem(_ *ast.ExprArrayItem) { 242 | // do nothing 243 | } 244 | 245 | func (v *Null) ExprArrowFunction(_ *ast.ExprArrowFunction) { 246 | // do nothing 247 | } 248 | 249 | func (v *Null) ExprBitwiseNot(_ *ast.ExprBitwiseNot) { 250 | // do nothing 251 | } 252 | 253 | func (v *Null) ExprBooleanNot(_ *ast.ExprBooleanNot) { 254 | // do nothing 255 | } 256 | 257 | func (v *Null) ExprBrackets(_ *ast.ExprBrackets) { 258 | // do nothing 259 | } 260 | 261 | func (v *Null) ExprClassConstFetch(_ *ast.ExprClassConstFetch) { 262 | // do nothing 263 | } 264 | 265 | func (v *Null) ExprClone(_ *ast.ExprClone) { 266 | // do nothing 267 | } 268 | 269 | func (v *Null) ExprClosure(_ *ast.ExprClosure) { 270 | // do nothing 271 | } 272 | 273 | func (v *Null) ExprClosureUse(_ *ast.ExprClosureUse) { 274 | // do nothing 275 | } 276 | 277 | func (v *Null) ExprConstFetch(_ *ast.ExprConstFetch) { 278 | // do nothing 279 | } 280 | 281 | func (v *Null) ExprEmpty(_ *ast.ExprEmpty) { 282 | // do nothing 283 | } 284 | 285 | func (v *Null) ExprErrorSuppress(_ *ast.ExprErrorSuppress) { 286 | // do nothing 287 | } 288 | 289 | func (v *Null) ExprEval(_ *ast.ExprEval) { 290 | // do nothing 291 | } 292 | 293 | func (v *Null) ExprExit(_ *ast.ExprExit) { 294 | // do nothing 295 | } 296 | 297 | func (v *Null) ExprFunctionCall(_ *ast.ExprFunctionCall) { 298 | // do nothing 299 | } 300 | 301 | func (v *Null) ExprInclude(_ *ast.ExprInclude) { 302 | // do nothing 303 | } 304 | 305 | func (v *Null) ExprIncludeOnce(_ *ast.ExprIncludeOnce) { 306 | // do nothing 307 | } 308 | 309 | func (v *Null) ExprInstanceOf(_ *ast.ExprInstanceOf) { 310 | // do nothing 311 | } 312 | 313 | func (v *Null) ExprIsset(_ *ast.ExprIsset) { 314 | // do nothing 315 | } 316 | 317 | func (v *Null) ExprList(_ *ast.ExprList) { 318 | // do nothing 319 | } 320 | 321 | func (v *Null) ExprMethodCall(_ *ast.ExprMethodCall) { 322 | // do nothing 323 | } 324 | 325 | func (v *Null) ExprNew(_ *ast.ExprNew) { 326 | // do nothing 327 | } 328 | 329 | func (v *Null) ExprPostDec(_ *ast.ExprPostDec) { 330 | // do nothing 331 | } 332 | 333 | func (v *Null) ExprPostInc(_ *ast.ExprPostInc) { 334 | // do nothing 335 | } 336 | 337 | func (v *Null) ExprPreDec(_ *ast.ExprPreDec) { 338 | // do nothing 339 | } 340 | 341 | func (v *Null) ExprPreInc(_ *ast.ExprPreInc) { 342 | // do nothing 343 | } 344 | 345 | func (v *Null) ExprPrint(_ *ast.ExprPrint) { 346 | // do nothing 347 | } 348 | 349 | func (v *Null) ExprPropertyFetch(_ *ast.ExprPropertyFetch) { 350 | // do nothing 351 | } 352 | 353 | func (v *Null) ExprRequire(_ *ast.ExprRequire) { 354 | // do nothing 355 | } 356 | 357 | func (v *Null) ExprRequireOnce(_ *ast.ExprRequireOnce) { 358 | // do nothing 359 | } 360 | 361 | func (v *Null) ExprShellExec(_ *ast.ExprShellExec) { 362 | // do nothing 363 | } 364 | 365 | func (v *Null) ExprStaticCall(_ *ast.ExprStaticCall) { 366 | // do nothing 367 | } 368 | 369 | func (v *Null) ExprStaticPropertyFetch(_ *ast.ExprStaticPropertyFetch) { 370 | // do nothing 371 | } 372 | 373 | func (v *Null) ExprTernary(_ *ast.ExprTernary) { 374 | // do nothing 375 | } 376 | 377 | func (v *Null) ExprUnaryMinus(_ *ast.ExprUnaryMinus) { 378 | // do nothing 379 | } 380 | 381 | func (v *Null) ExprUnaryPlus(_ *ast.ExprUnaryPlus) { 382 | // do nothing 383 | } 384 | 385 | func (v *Null) ExprVariable(_ *ast.ExprVariable) { 386 | // do nothing 387 | } 388 | 389 | func (v *Null) ExprYield(_ *ast.ExprYield) { 390 | // do nothing 391 | } 392 | 393 | func (v *Null) ExprYieldFrom(_ *ast.ExprYieldFrom) { 394 | // do nothing 395 | } 396 | 397 | func (v *Null) ExprAssign(_ *ast.ExprAssign) { 398 | // do nothing 399 | } 400 | 401 | func (v *Null) ExprAssignReference(_ *ast.ExprAssignReference) { 402 | // do nothing 403 | } 404 | 405 | func (v *Null) ExprAssignBitwiseAnd(_ *ast.ExprAssignBitwiseAnd) { 406 | // do nothing 407 | } 408 | 409 | func (v *Null) ExprAssignBitwiseOr(_ *ast.ExprAssignBitwiseOr) { 410 | // do nothing 411 | } 412 | 413 | func (v *Null) ExprAssignBitwiseXor(_ *ast.ExprAssignBitwiseXor) { 414 | // do nothing 415 | } 416 | 417 | func (v *Null) ExprAssignCoalesce(_ *ast.ExprAssignCoalesce) { 418 | // do nothing 419 | } 420 | 421 | func (v *Null) ExprAssignConcat(_ *ast.ExprAssignConcat) { 422 | // do nothing 423 | } 424 | 425 | func (v *Null) ExprAssignDiv(_ *ast.ExprAssignDiv) { 426 | // do nothing 427 | } 428 | 429 | func (v *Null) ExprAssignMinus(_ *ast.ExprAssignMinus) { 430 | // do nothing 431 | } 432 | 433 | func (v *Null) ExprAssignMod(_ *ast.ExprAssignMod) { 434 | // do nothing 435 | } 436 | 437 | func (v *Null) ExprAssignMul(_ *ast.ExprAssignMul) { 438 | // do nothing 439 | } 440 | 441 | func (v *Null) ExprAssignPlus(_ *ast.ExprAssignPlus) { 442 | // do nothing 443 | } 444 | 445 | func (v *Null) ExprAssignPow(_ *ast.ExprAssignPow) { 446 | // do nothing 447 | } 448 | 449 | func (v *Null) ExprAssignShiftLeft(_ *ast.ExprAssignShiftLeft) { 450 | // do nothing 451 | } 452 | 453 | func (v *Null) ExprAssignShiftRight(_ *ast.ExprAssignShiftRight) { 454 | // do nothing 455 | } 456 | 457 | func (v *Null) ExprBinaryBitwiseAnd(_ *ast.ExprBinaryBitwiseAnd) { 458 | // do nothing 459 | } 460 | 461 | func (v *Null) ExprBinaryBitwiseOr(_ *ast.ExprBinaryBitwiseOr) { 462 | // do nothing 463 | } 464 | 465 | func (v *Null) ExprBinaryBitwiseXor(_ *ast.ExprBinaryBitwiseXor) { 466 | // do nothing 467 | } 468 | 469 | func (v *Null) ExprBinaryBooleanAnd(_ *ast.ExprBinaryBooleanAnd) { 470 | // do nothing 471 | } 472 | 473 | func (v *Null) ExprBinaryBooleanOr(_ *ast.ExprBinaryBooleanOr) { 474 | // do nothing 475 | } 476 | 477 | func (v *Null) ExprBinaryCoalesce(_ *ast.ExprBinaryCoalesce) { 478 | // do nothing 479 | } 480 | 481 | func (v *Null) ExprBinaryConcat(_ *ast.ExprBinaryConcat) { 482 | // do nothing 483 | } 484 | 485 | func (v *Null) ExprBinaryDiv(_ *ast.ExprBinaryDiv) { 486 | // do nothing 487 | } 488 | 489 | func (v *Null) ExprBinaryEqual(_ *ast.ExprBinaryEqual) { 490 | // do nothing 491 | } 492 | 493 | func (v *Null) ExprBinaryGreater(_ *ast.ExprBinaryGreater) { 494 | // do nothing 495 | } 496 | 497 | func (v *Null) ExprBinaryGreaterOrEqual(_ *ast.ExprBinaryGreaterOrEqual) { 498 | // do nothing 499 | } 500 | 501 | func (v *Null) ExprBinaryIdentical(_ *ast.ExprBinaryIdentical) { 502 | // do nothing 503 | } 504 | 505 | func (v *Null) ExprBinaryLogicalAnd(_ *ast.ExprBinaryLogicalAnd) { 506 | // do nothing 507 | } 508 | 509 | func (v *Null) ExprBinaryLogicalOr(_ *ast.ExprBinaryLogicalOr) { 510 | // do nothing 511 | } 512 | 513 | func (v *Null) ExprBinaryLogicalXor(_ *ast.ExprBinaryLogicalXor) { 514 | // do nothing 515 | } 516 | 517 | func (v *Null) ExprBinaryMinus(_ *ast.ExprBinaryMinus) { 518 | // do nothing 519 | } 520 | 521 | func (v *Null) ExprBinaryMod(_ *ast.ExprBinaryMod) { 522 | // do nothing 523 | } 524 | 525 | func (v *Null) ExprBinaryMul(_ *ast.ExprBinaryMul) { 526 | // do nothing 527 | } 528 | 529 | func (v *Null) ExprBinaryNotEqual(_ *ast.ExprBinaryNotEqual) { 530 | // do nothing 531 | } 532 | 533 | func (v *Null) ExprBinaryNotIdentical(_ *ast.ExprBinaryNotIdentical) { 534 | // do nothing 535 | } 536 | 537 | func (v *Null) ExprBinaryPlus(_ *ast.ExprBinaryPlus) { 538 | // do nothing 539 | } 540 | 541 | func (v *Null) ExprBinaryPow(_ *ast.ExprBinaryPow) { 542 | // do nothing 543 | } 544 | 545 | func (v *Null) ExprBinaryShiftLeft(_ *ast.ExprBinaryShiftLeft) { 546 | // do nothing 547 | } 548 | 549 | func (v *Null) ExprBinaryShiftRight(_ *ast.ExprBinaryShiftRight) { 550 | // do nothing 551 | } 552 | 553 | func (v *Null) ExprBinarySmaller(_ *ast.ExprBinarySmaller) { 554 | // do nothing 555 | } 556 | 557 | func (v *Null) ExprBinarySmallerOrEqual(_ *ast.ExprBinarySmallerOrEqual) { 558 | // do nothing 559 | } 560 | 561 | func (v *Null) ExprBinarySpaceship(_ *ast.ExprBinarySpaceship) { 562 | // do nothing 563 | } 564 | 565 | func (v *Null) ExprCastArray(_ *ast.ExprCastArray) { 566 | // do nothing 567 | } 568 | 569 | func (v *Null) ExprCastBool(_ *ast.ExprCastBool) { 570 | // do nothing 571 | } 572 | 573 | func (v *Null) ExprCastDouble(_ *ast.ExprCastDouble) { 574 | // do nothing 575 | } 576 | 577 | func (v *Null) ExprCastInt(_ *ast.ExprCastInt) { 578 | // do nothing 579 | } 580 | 581 | func (v *Null) ExprCastObject(_ *ast.ExprCastObject) { 582 | // do nothing 583 | } 584 | 585 | func (v *Null) ExprCastString(_ *ast.ExprCastString) { 586 | // do nothing 587 | } 588 | 589 | func (v *Null) ExprCastUnset(_ *ast.ExprCastUnset) { 590 | // do nothing 591 | } 592 | 593 | func (v *Null) ScalarDnumber(_ *ast.ScalarDnumber) { 594 | // do nothing 595 | } 596 | 597 | func (v *Null) ScalarEncapsed(_ *ast.ScalarEncapsed) { 598 | // do nothing 599 | } 600 | 601 | func (v *Null) ScalarEncapsedStringPart(_ *ast.ScalarEncapsedStringPart) { 602 | // do nothing 603 | } 604 | 605 | func (v *Null) ScalarEncapsedStringBrackets(_ *ast.ScalarEncapsedStringBrackets) { 606 | // do nothing 607 | } 608 | 609 | func (v *Null) ScalarEncapsedStringVar(_ *ast.ScalarEncapsedStringVar) { 610 | // do nothing 611 | } 612 | 613 | func (v *Null) ScalarHeredoc(_ *ast.ScalarHeredoc) { 614 | // do nothing 615 | } 616 | 617 | func (v *Null) ScalarLnumber(_ *ast.ScalarLnumber) { 618 | // do nothing 619 | } 620 | 621 | func (v *Null) ScalarMagicConstant(_ *ast.ScalarMagicConstant) { 622 | // do nothing 623 | } 624 | 625 | func (v *Null) ScalarString(_ *ast.ScalarString) { 626 | // do nothing 627 | } 628 | 629 | func (v *Null) NameName(_ *ast.Name) { 630 | // do nothing 631 | } 632 | 633 | func (v *Null) NameFullyQualified(_ *ast.NameFullyQualified) { 634 | // do nothing 635 | } 636 | 637 | func (v *Null) NameRelative(_ *ast.NameRelative) { 638 | // do nothing 639 | } 640 | 641 | func (v *Null) NameNamePart(_ *ast.NamePart) { 642 | // do nothing 643 | } 644 | -------------------------------------------------------------------------------- /pkg/visitor/traverser/traverser.go: -------------------------------------------------------------------------------- 1 | package traverser 2 | 3 | import ( 4 | "github.com/z7zmey/php-parser/pkg/ast" 5 | ) 6 | 7 | type Traverser struct { 8 | v ast.Visitor 9 | } 10 | 11 | func NewTraverser(v ast.Visitor) *Traverser { 12 | return &Traverser{ 13 | v: v, 14 | } 15 | } 16 | 17 | func (t *Traverser) Traverse(n ast.Vertex) { 18 | if n != nil { 19 | n.Accept(t) 20 | } 21 | } 22 | 23 | func (t *Traverser) Root(n *ast.Root) { 24 | n.Accept(t.v) 25 | 26 | for _, nn := range n.Stmts { 27 | nn.Accept(t) 28 | } 29 | } 30 | 31 | func (t *Traverser) Nullable(n *ast.Nullable) { 32 | n.Accept(t.v) 33 | 34 | t.Traverse(n.Expr) 35 | } 36 | 37 | func (t *Traverser) Parameter(n *ast.Parameter) { 38 | n.Accept(t.v) 39 | 40 | t.Traverse(n.Type) 41 | t.Traverse(n.Var) 42 | t.Traverse(n.DefaultValue) 43 | } 44 | 45 | func (t *Traverser) Identifier(n *ast.Identifier) { 46 | n.Accept(t.v) 47 | } 48 | 49 | func (t *Traverser) Argument(n *ast.Argument) { 50 | n.Accept(t.v) 51 | 52 | t.Traverse(n.Expr) 53 | } 54 | 55 | func (t *Traverser) StmtBreak(n *ast.StmtBreak) { 56 | n.Accept(t.v) 57 | 58 | t.Traverse(n.Expr) 59 | } 60 | 61 | func (t *Traverser) StmtCase(n *ast.StmtCase) { 62 | n.Accept(t.v) 63 | 64 | t.Traverse(n.Cond) 65 | for _, nn := range n.Stmts { 66 | nn.Accept(t) 67 | } 68 | } 69 | 70 | func (t *Traverser) StmtCatch(n *ast.StmtCatch) { 71 | n.Accept(t.v) 72 | 73 | for _, nn := range n.Types { 74 | nn.Accept(t) 75 | } 76 | t.Traverse(n.Var) 77 | for _, nn := range n.Stmts { 78 | nn.Accept(t) 79 | } 80 | } 81 | 82 | func (t *Traverser) StmtClass(n *ast.StmtClass) { 83 | n.Accept(t.v) 84 | 85 | for _, nn := range n.Modifiers { 86 | nn.Accept(t) 87 | } 88 | t.Traverse(n.Name) 89 | for _, nn := range n.Args { 90 | nn.Accept(t) 91 | } 92 | t.Traverse(n.Extends) 93 | for _, nn := range n.Implements { 94 | nn.Accept(t) 95 | } 96 | for _, nn := range n.Stmts { 97 | nn.Accept(t) 98 | } 99 | } 100 | 101 | func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) { 102 | n.Accept(t.v) 103 | 104 | for _, nn := range n.Modifiers { 105 | nn.Accept(t) 106 | } 107 | for _, nn := range n.Consts { 108 | nn.Accept(t) 109 | } 110 | } 111 | 112 | func (t *Traverser) StmtClassMethod(n *ast.StmtClassMethod) { 113 | n.Accept(t.v) 114 | 115 | for _, nn := range n.Modifiers { 116 | nn.Accept(t) 117 | } 118 | t.Traverse(n.Name) 119 | for _, nn := range n.Params { 120 | nn.Accept(t) 121 | } 122 | t.Traverse(n.ReturnType) 123 | t.Traverse(n.Stmt) 124 | } 125 | 126 | func (t *Traverser) StmtConstList(n *ast.StmtConstList) { 127 | n.Accept(t.v) 128 | 129 | for _, nn := range n.Consts { 130 | nn.Accept(t) 131 | } 132 | } 133 | 134 | func (t *Traverser) StmtConstant(n *ast.StmtConstant) { 135 | n.Accept(t.v) 136 | 137 | t.Traverse(n.Name) 138 | t.Traverse(n.Expr) 139 | } 140 | 141 | func (t *Traverser) StmtContinue(n *ast.StmtContinue) { 142 | n.Accept(t.v) 143 | 144 | t.Traverse(n.Expr) 145 | } 146 | 147 | func (t *Traverser) StmtDeclare(n *ast.StmtDeclare) { 148 | n.Accept(t.v) 149 | 150 | for _, nn := range n.Consts { 151 | nn.Accept(t) 152 | } 153 | t.Traverse(n.Stmt) 154 | } 155 | 156 | func (t *Traverser) StmtDefault(n *ast.StmtDefault) { 157 | n.Accept(t.v) 158 | 159 | for _, nn := range n.Stmts { 160 | nn.Accept(t) 161 | } 162 | } 163 | 164 | func (t *Traverser) StmtDo(n *ast.StmtDo) { 165 | n.Accept(t.v) 166 | 167 | t.Traverse(n.Stmt) 168 | t.Traverse(n.Cond) 169 | } 170 | 171 | func (t *Traverser) StmtEcho(n *ast.StmtEcho) { 172 | n.Accept(t.v) 173 | 174 | for _, nn := range n.Exprs { 175 | nn.Accept(t) 176 | } 177 | } 178 | 179 | func (t *Traverser) StmtElse(n *ast.StmtElse) { 180 | n.Accept(t.v) 181 | 182 | t.Traverse(n.Stmt) 183 | } 184 | 185 | func (t *Traverser) StmtElseIf(n *ast.StmtElseIf) { 186 | n.Accept(t.v) 187 | 188 | t.Traverse(n.Cond) 189 | t.Traverse(n.Stmt) 190 | } 191 | 192 | func (t *Traverser) StmtExpression(n *ast.StmtExpression) { 193 | n.Accept(t.v) 194 | 195 | t.Traverse(n.Expr) 196 | } 197 | 198 | func (t *Traverser) StmtFinally(n *ast.StmtFinally) { 199 | n.Accept(t.v) 200 | 201 | for _, nn := range n.Stmts { 202 | nn.Accept(t) 203 | } 204 | } 205 | 206 | func (t *Traverser) StmtFor(n *ast.StmtFor) { 207 | n.Accept(t.v) 208 | 209 | for _, nn := range n.Init { 210 | nn.Accept(t) 211 | } 212 | for _, nn := range n.Cond { 213 | nn.Accept(t) 214 | } 215 | for _, nn := range n.Loop { 216 | nn.Accept(t) 217 | } 218 | t.Traverse(n.Stmt) 219 | } 220 | 221 | func (t *Traverser) StmtForeach(n *ast.StmtForeach) { 222 | n.Accept(t.v) 223 | 224 | t.Traverse(n.Expr) 225 | t.Traverse(n.Key) 226 | t.Traverse(n.Var) 227 | t.Traverse(n.Stmt) 228 | } 229 | 230 | func (t *Traverser) StmtFunction(n *ast.StmtFunction) { 231 | n.Accept(t.v) 232 | 233 | t.Traverse(n.Name) 234 | for _, nn := range n.Params { 235 | nn.Accept(t) 236 | } 237 | t.Traverse(n.ReturnType) 238 | for _, nn := range n.Stmts { 239 | nn.Accept(t) 240 | } 241 | } 242 | 243 | func (t *Traverser) StmtGlobal(n *ast.StmtGlobal) { 244 | n.Accept(t.v) 245 | 246 | for _, nn := range n.Vars { 247 | nn.Accept(t) 248 | } 249 | } 250 | 251 | func (t *Traverser) StmtGoto(n *ast.StmtGoto) { 252 | n.Accept(t.v) 253 | 254 | t.Traverse(n.Label) 255 | } 256 | 257 | func (t *Traverser) StmtHaltCompiler(n *ast.StmtHaltCompiler) { 258 | n.Accept(t.v) 259 | } 260 | 261 | func (t *Traverser) StmtIf(n *ast.StmtIf) { 262 | n.Accept(t.v) 263 | 264 | t.Traverse(n.Cond) 265 | t.Traverse(n.Stmt) 266 | for _, nn := range n.ElseIf { 267 | nn.Accept(t) 268 | } 269 | t.Traverse(n.Else) 270 | } 271 | 272 | func (t *Traverser) StmtInlineHtml(n *ast.StmtInlineHtml) { 273 | n.Accept(t.v) 274 | } 275 | 276 | func (t *Traverser) StmtInterface(n *ast.StmtInterface) { 277 | n.Accept(t.v) 278 | 279 | t.Traverse(n.Name) 280 | for _, nn := range n.Extends { 281 | nn.Accept(t) 282 | } 283 | for _, nn := range n.Stmts { 284 | nn.Accept(t) 285 | } 286 | } 287 | 288 | func (t *Traverser) StmtLabel(n *ast.StmtLabel) { 289 | n.Accept(t.v) 290 | 291 | t.Traverse(n.Name) 292 | } 293 | 294 | func (t *Traverser) StmtNamespace(n *ast.StmtNamespace) { 295 | n.Accept(t.v) 296 | 297 | t.Traverse(n.Name) 298 | for _, nn := range n.Stmts { 299 | nn.Accept(t) 300 | } 301 | } 302 | 303 | func (t *Traverser) StmtNop(n *ast.StmtNop) { 304 | n.Accept(t.v) 305 | } 306 | 307 | func (t *Traverser) StmtProperty(n *ast.StmtProperty) { 308 | n.Accept(t.v) 309 | 310 | t.Traverse(n.Var) 311 | t.Traverse(n.Expr) 312 | } 313 | 314 | func (t *Traverser) StmtPropertyList(n *ast.StmtPropertyList) { 315 | n.Accept(t.v) 316 | 317 | for _, nn := range n.Modifiers { 318 | nn.Accept(t) 319 | } 320 | t.Traverse(n.Type) 321 | for _, nn := range n.Props { 322 | nn.Accept(t) 323 | } 324 | } 325 | 326 | func (t *Traverser) StmtReturn(n *ast.StmtReturn) { 327 | n.Accept(t.v) 328 | 329 | t.Traverse(n.Expr) 330 | } 331 | 332 | func (t *Traverser) StmtStatic(n *ast.StmtStatic) { 333 | n.Accept(t.v) 334 | 335 | for _, nn := range n.Vars { 336 | nn.Accept(t) 337 | } 338 | } 339 | 340 | func (t *Traverser) StmtStaticVar(n *ast.StmtStaticVar) { 341 | n.Accept(t.v) 342 | 343 | t.Traverse(n.Var) 344 | t.Traverse(n.Expr) 345 | } 346 | 347 | func (t *Traverser) StmtStmtList(n *ast.StmtStmtList) { 348 | n.Accept(t.v) 349 | 350 | for _, nn := range n.Stmts { 351 | nn.Accept(t) 352 | } 353 | } 354 | 355 | func (t *Traverser) StmtSwitch(n *ast.StmtSwitch) { 356 | n.Accept(t.v) 357 | 358 | t.Traverse(n.Cond) 359 | for _, nn := range n.Cases { 360 | nn.Accept(t) 361 | } 362 | } 363 | 364 | func (t *Traverser) StmtThrow(n *ast.StmtThrow) { 365 | n.Accept(t.v) 366 | 367 | t.Traverse(n.Expr) 368 | } 369 | 370 | func (t *Traverser) StmtTrait(n *ast.StmtTrait) { 371 | n.Accept(t.v) 372 | 373 | t.Traverse(n.Name) 374 | for _, nn := range n.Stmts { 375 | nn.Accept(t) 376 | } 377 | } 378 | 379 | func (t *Traverser) StmtTraitUse(n *ast.StmtTraitUse) { 380 | n.Accept(t.v) 381 | 382 | for _, nn := range n.Traits { 383 | nn.Accept(t) 384 | } 385 | for _, nn := range n.Adaptations { 386 | nn.Accept(t) 387 | } 388 | } 389 | 390 | func (t *Traverser) StmtTraitUseAlias(n *ast.StmtTraitUseAlias) { 391 | n.Accept(t.v) 392 | 393 | t.Traverse(n.Trait) 394 | t.Traverse(n.Method) 395 | t.Traverse(n.Modifier) 396 | t.Traverse(n.Alias) 397 | } 398 | 399 | func (t *Traverser) StmtTraitUsePrecedence(n *ast.StmtTraitUsePrecedence) { 400 | n.Accept(t.v) 401 | 402 | t.Traverse(n.Trait) 403 | t.Traverse(n.Method) 404 | for _, nn := range n.Insteadof { 405 | nn.Accept(t) 406 | } 407 | } 408 | 409 | func (t *Traverser) StmtTry(n *ast.StmtTry) { 410 | n.Accept(t.v) 411 | 412 | for _, nn := range n.Stmts { 413 | nn.Accept(t) 414 | } 415 | for _, nn := range n.Catches { 416 | nn.Accept(t) 417 | } 418 | t.Traverse(n.Finally) 419 | } 420 | 421 | func (t *Traverser) StmtUnset(n *ast.StmtUnset) { 422 | n.Accept(t.v) 423 | 424 | for _, nn := range n.Vars { 425 | nn.Accept(t) 426 | } 427 | } 428 | 429 | func (t *Traverser) StmtUse(n *ast.StmtUseList) { 430 | n.Accept(t.v) 431 | 432 | t.Traverse(n.Type) 433 | for _, nn := range n.Uses { 434 | nn.Accept(t) 435 | } 436 | } 437 | 438 | func (t *Traverser) StmtGroupUse(n *ast.StmtGroupUseList) { 439 | n.Accept(t.v) 440 | 441 | t.Traverse(n.Type) 442 | t.Traverse(n.Prefix) 443 | for _, nn := range n.Uses { 444 | nn.Accept(t) 445 | } 446 | } 447 | 448 | func (t *Traverser) StmtUseDeclaration(n *ast.StmtUse) { 449 | n.Accept(t.v) 450 | 451 | t.Traverse(n.Type) 452 | t.Traverse(n.Use) 453 | t.Traverse(n.Alias) 454 | } 455 | 456 | func (t *Traverser) StmtWhile(n *ast.StmtWhile) { 457 | n.Accept(t.v) 458 | 459 | t.Traverse(n.Cond) 460 | t.Traverse(n.Stmt) 461 | } 462 | 463 | func (t *Traverser) ExprArray(n *ast.ExprArray) { 464 | n.Accept(t.v) 465 | 466 | for _, nn := range n.Items { 467 | nn.Accept(t) 468 | } 469 | } 470 | 471 | func (t *Traverser) ExprArrayDimFetch(n *ast.ExprArrayDimFetch) { 472 | n.Accept(t.v) 473 | 474 | t.Traverse(n.Var) 475 | t.Traverse(n.Dim) 476 | } 477 | 478 | func (t *Traverser) ExprArrayItem(n *ast.ExprArrayItem) { 479 | n.Accept(t.v) 480 | 481 | t.Traverse(n.Key) 482 | t.Traverse(n.Val) 483 | } 484 | 485 | func (t *Traverser) ExprArrowFunction(n *ast.ExprArrowFunction) { 486 | n.Accept(t.v) 487 | 488 | for _, nn := range n.Params { 489 | nn.Accept(t) 490 | } 491 | t.Traverse(n.ReturnType) 492 | t.Traverse(n.Expr) 493 | } 494 | 495 | func (t *Traverser) ExprBitwiseNot(n *ast.ExprBitwiseNot) { 496 | n.Accept(t.v) 497 | 498 | t.Traverse(n.Expr) 499 | } 500 | 501 | func (t *Traverser) ExprBooleanNot(n *ast.ExprBooleanNot) { 502 | n.Accept(t.v) 503 | 504 | t.Traverse(n.Expr) 505 | } 506 | 507 | func (t *Traverser) ExprBrackets(n *ast.ExprBrackets) { 508 | n.Accept(t.v) 509 | 510 | t.Traverse(n.Expr) 511 | } 512 | 513 | func (t *Traverser) ExprClassConstFetch(n *ast.ExprClassConstFetch) { 514 | n.Accept(t.v) 515 | 516 | t.Traverse(n.Class) 517 | t.Traverse(n.Const) 518 | } 519 | 520 | func (t *Traverser) ExprClone(n *ast.ExprClone) { 521 | n.Accept(t.v) 522 | 523 | t.Traverse(n.Expr) 524 | } 525 | 526 | func (t *Traverser) ExprClosure(n *ast.ExprClosure) { 527 | n.Accept(t.v) 528 | 529 | for _, nn := range n.Params { 530 | nn.Accept(t) 531 | } 532 | for _, nn := range n.Uses { 533 | nn.Accept(t) 534 | } 535 | t.Traverse(n.ReturnType) 536 | for _, nn := range n.Stmts { 537 | nn.Accept(t) 538 | } 539 | } 540 | 541 | func (t *Traverser) ExprClosureUse(n *ast.ExprClosureUse) { 542 | n.Accept(t.v) 543 | 544 | t.Traverse(n.Var) 545 | } 546 | 547 | func (t *Traverser) ExprConstFetch(n *ast.ExprConstFetch) { 548 | n.Accept(t.v) 549 | 550 | t.Traverse(n.Const) 551 | } 552 | 553 | func (t *Traverser) ExprEmpty(n *ast.ExprEmpty) { 554 | n.Accept(t.v) 555 | 556 | t.Traverse(n.Expr) 557 | } 558 | 559 | func (t *Traverser) ExprErrorSuppress(n *ast.ExprErrorSuppress) { 560 | n.Accept(t.v) 561 | 562 | t.Traverse(n.Expr) 563 | } 564 | 565 | func (t *Traverser) ExprEval(n *ast.ExprEval) { 566 | n.Accept(t.v) 567 | 568 | t.Traverse(n.Expr) 569 | } 570 | 571 | func (t *Traverser) ExprExit(n *ast.ExprExit) { 572 | n.Accept(t.v) 573 | 574 | t.Traverse(n.Expr) 575 | } 576 | 577 | func (t *Traverser) ExprFunctionCall(n *ast.ExprFunctionCall) { 578 | n.Accept(t.v) 579 | 580 | t.Traverse(n.Function) 581 | for _, nn := range n.Args { 582 | nn.Accept(t) 583 | } 584 | } 585 | 586 | func (t *Traverser) ExprInclude(n *ast.ExprInclude) { 587 | n.Accept(t.v) 588 | 589 | t.Traverse(n.Expr) 590 | } 591 | 592 | func (t *Traverser) ExprIncludeOnce(n *ast.ExprIncludeOnce) { 593 | n.Accept(t.v) 594 | 595 | t.Traverse(n.Expr) 596 | } 597 | 598 | func (t *Traverser) ExprInstanceOf(n *ast.ExprInstanceOf) { 599 | n.Accept(t.v) 600 | 601 | t.Traverse(n.Expr) 602 | t.Traverse(n.Class) 603 | } 604 | 605 | func (t *Traverser) ExprIsset(n *ast.ExprIsset) { 606 | n.Accept(t.v) 607 | 608 | for _, nn := range n.Vars { 609 | nn.Accept(t) 610 | } 611 | } 612 | 613 | func (t *Traverser) ExprList(n *ast.ExprList) { 614 | n.Accept(t.v) 615 | 616 | for _, nn := range n.Items { 617 | nn.Accept(t) 618 | } 619 | } 620 | 621 | func (t *Traverser) ExprMethodCall(n *ast.ExprMethodCall) { 622 | n.Accept(t.v) 623 | 624 | t.Traverse(n.Var) 625 | t.Traverse(n.Method) 626 | for _, nn := range n.Args { 627 | nn.Accept(t) 628 | } 629 | } 630 | 631 | func (t *Traverser) ExprNew(n *ast.ExprNew) { 632 | n.Accept(t.v) 633 | 634 | t.Traverse(n.Class) 635 | for _, nn := range n.Args { 636 | nn.Accept(t) 637 | } 638 | } 639 | 640 | func (t *Traverser) ExprPostDec(n *ast.ExprPostDec) { 641 | n.Accept(t.v) 642 | 643 | t.Traverse(n.Var) 644 | } 645 | 646 | func (t *Traverser) ExprPostInc(n *ast.ExprPostInc) { 647 | n.Accept(t.v) 648 | 649 | t.Traverse(n.Var) 650 | } 651 | 652 | func (t *Traverser) ExprPreDec(n *ast.ExprPreDec) { 653 | n.Accept(t.v) 654 | 655 | t.Traverse(n.Var) 656 | } 657 | 658 | func (t *Traverser) ExprPreInc(n *ast.ExprPreInc) { 659 | n.Accept(t.v) 660 | 661 | t.Traverse(n.Var) 662 | } 663 | 664 | func (t *Traverser) ExprPrint(n *ast.ExprPrint) { 665 | n.Accept(t.v) 666 | 667 | t.Traverse(n.Expr) 668 | } 669 | 670 | func (t *Traverser) ExprPropertyFetch(n *ast.ExprPropertyFetch) { 671 | n.Accept(t.v) 672 | 673 | t.Traverse(n.Var) 674 | t.Traverse(n.Prop) 675 | } 676 | 677 | func (t *Traverser) ExprRequire(n *ast.ExprRequire) { 678 | n.Accept(t.v) 679 | 680 | t.Traverse(n.Expr) 681 | } 682 | 683 | func (t *Traverser) ExprRequireOnce(n *ast.ExprRequireOnce) { 684 | n.Accept(t.v) 685 | 686 | t.Traverse(n.Expr) 687 | } 688 | 689 | func (t *Traverser) ExprShellExec(n *ast.ExprShellExec) { 690 | n.Accept(t.v) 691 | 692 | for _, nn := range n.Parts { 693 | nn.Accept(t) 694 | } 695 | } 696 | 697 | func (t *Traverser) ExprStaticCall(n *ast.ExprStaticCall) { 698 | n.Accept(t.v) 699 | 700 | t.Traverse(n.Class) 701 | t.Traverse(n.Call) 702 | for _, nn := range n.Args { 703 | nn.Accept(t) 704 | } 705 | } 706 | 707 | func (t *Traverser) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) { 708 | n.Accept(t.v) 709 | 710 | t.Traverse(n.Class) 711 | t.Traverse(n.Prop) 712 | } 713 | 714 | func (t *Traverser) ExprTernary(n *ast.ExprTernary) { 715 | n.Accept(t.v) 716 | 717 | t.Traverse(n.Cond) 718 | t.Traverse(n.IfTrue) 719 | t.Traverse(n.IfFalse) 720 | } 721 | 722 | func (t *Traverser) ExprUnaryMinus(n *ast.ExprUnaryMinus) { 723 | n.Accept(t.v) 724 | 725 | t.Traverse(n.Expr) 726 | } 727 | 728 | func (t *Traverser) ExprUnaryPlus(n *ast.ExprUnaryPlus) { 729 | n.Accept(t.v) 730 | 731 | t.Traverse(n.Expr) 732 | } 733 | 734 | func (t *Traverser) ExprVariable(n *ast.ExprVariable) { 735 | n.Accept(t.v) 736 | 737 | t.Traverse(n.Name) 738 | } 739 | 740 | func (t *Traverser) ExprYield(n *ast.ExprYield) { 741 | n.Accept(t.v) 742 | 743 | t.Traverse(n.Key) 744 | t.Traverse(n.Val) 745 | } 746 | 747 | func (t *Traverser) ExprYieldFrom(n *ast.ExprYieldFrom) { 748 | n.Accept(t.v) 749 | 750 | t.Traverse(n.Expr) 751 | } 752 | 753 | func (t *Traverser) ExprAssign(n *ast.ExprAssign) { 754 | n.Accept(t.v) 755 | 756 | t.Traverse(n.Var) 757 | t.Traverse(n.Expr) 758 | } 759 | 760 | func (t *Traverser) ExprAssignReference(n *ast.ExprAssignReference) { 761 | n.Accept(t.v) 762 | 763 | t.Traverse(n.Var) 764 | t.Traverse(n.Expr) 765 | } 766 | 767 | func (t *Traverser) ExprAssignBitwiseAnd(n *ast.ExprAssignBitwiseAnd) { 768 | n.Accept(t.v) 769 | 770 | t.Traverse(n.Var) 771 | t.Traverse(n.Expr) 772 | } 773 | 774 | func (t *Traverser) ExprAssignBitwiseOr(n *ast.ExprAssignBitwiseOr) { 775 | n.Accept(t.v) 776 | 777 | t.Traverse(n.Var) 778 | t.Traverse(n.Expr) 779 | } 780 | 781 | func (t *Traverser) ExprAssignBitwiseXor(n *ast.ExprAssignBitwiseXor) { 782 | n.Accept(t.v) 783 | 784 | t.Traverse(n.Var) 785 | t.Traverse(n.Expr) 786 | } 787 | 788 | func (t *Traverser) ExprAssignCoalesce(n *ast.ExprAssignCoalesce) { 789 | n.Accept(t.v) 790 | 791 | t.Traverse(n.Var) 792 | t.Traverse(n.Expr) 793 | } 794 | 795 | func (t *Traverser) ExprAssignConcat(n *ast.ExprAssignConcat) { 796 | n.Accept(t.v) 797 | 798 | t.Traverse(n.Var) 799 | t.Traverse(n.Expr) 800 | } 801 | 802 | func (t *Traverser) ExprAssignDiv(n *ast.ExprAssignDiv) { 803 | n.Accept(t.v) 804 | 805 | t.Traverse(n.Var) 806 | t.Traverse(n.Expr) 807 | } 808 | 809 | func (t *Traverser) ExprAssignMinus(n *ast.ExprAssignMinus) { 810 | n.Accept(t.v) 811 | 812 | t.Traverse(n.Var) 813 | t.Traverse(n.Expr) 814 | } 815 | 816 | func (t *Traverser) ExprAssignMod(n *ast.ExprAssignMod) { 817 | n.Accept(t.v) 818 | 819 | t.Traverse(n.Var) 820 | t.Traverse(n.Expr) 821 | } 822 | 823 | func (t *Traverser) ExprAssignMul(n *ast.ExprAssignMul) { 824 | n.Accept(t.v) 825 | 826 | t.Traverse(n.Var) 827 | t.Traverse(n.Expr) 828 | } 829 | 830 | func (t *Traverser) ExprAssignPlus(n *ast.ExprAssignPlus) { 831 | n.Accept(t.v) 832 | 833 | t.Traverse(n.Var) 834 | t.Traverse(n.Expr) 835 | } 836 | 837 | func (t *Traverser) ExprAssignPow(n *ast.ExprAssignPow) { 838 | n.Accept(t.v) 839 | 840 | t.Traverse(n.Var) 841 | t.Traverse(n.Expr) 842 | } 843 | 844 | func (t *Traverser) ExprAssignShiftLeft(n *ast.ExprAssignShiftLeft) { 845 | n.Accept(t.v) 846 | 847 | t.Traverse(n.Var) 848 | t.Traverse(n.Expr) 849 | } 850 | 851 | func (t *Traverser) ExprAssignShiftRight(n *ast.ExprAssignShiftRight) { 852 | n.Accept(t.v) 853 | 854 | t.Traverse(n.Var) 855 | t.Traverse(n.Expr) 856 | } 857 | 858 | func (t *Traverser) ExprBinaryBitwiseAnd(n *ast.ExprBinaryBitwiseAnd) { 859 | n.Accept(t.v) 860 | 861 | t.Traverse(n.Left) 862 | t.Traverse(n.Right) 863 | } 864 | 865 | func (t *Traverser) ExprBinaryBitwiseOr(n *ast.ExprBinaryBitwiseOr) { 866 | n.Accept(t.v) 867 | 868 | t.Traverse(n.Left) 869 | t.Traverse(n.Right) 870 | } 871 | 872 | func (t *Traverser) ExprBinaryBitwiseXor(n *ast.ExprBinaryBitwiseXor) { 873 | n.Accept(t.v) 874 | 875 | t.Traverse(n.Left) 876 | t.Traverse(n.Right) 877 | } 878 | 879 | func (t *Traverser) ExprBinaryBooleanAnd(n *ast.ExprBinaryBooleanAnd) { 880 | n.Accept(t.v) 881 | 882 | t.Traverse(n.Left) 883 | t.Traverse(n.Right) 884 | } 885 | 886 | func (t *Traverser) ExprBinaryBooleanOr(n *ast.ExprBinaryBooleanOr) { 887 | n.Accept(t.v) 888 | 889 | t.Traverse(n.Left) 890 | t.Traverse(n.Right) 891 | } 892 | 893 | func (t *Traverser) ExprBinaryCoalesce(n *ast.ExprBinaryCoalesce) { 894 | n.Accept(t.v) 895 | 896 | t.Traverse(n.Left) 897 | t.Traverse(n.Right) 898 | } 899 | 900 | func (t *Traverser) ExprBinaryConcat(n *ast.ExprBinaryConcat) { 901 | n.Accept(t.v) 902 | 903 | t.Traverse(n.Left) 904 | t.Traverse(n.Right) 905 | } 906 | 907 | func (t *Traverser) ExprBinaryDiv(n *ast.ExprBinaryDiv) { 908 | n.Accept(t.v) 909 | 910 | t.Traverse(n.Left) 911 | t.Traverse(n.Right) 912 | } 913 | 914 | func (t *Traverser) ExprBinaryEqual(n *ast.ExprBinaryEqual) { 915 | n.Accept(t.v) 916 | 917 | t.Traverse(n.Left) 918 | t.Traverse(n.Right) 919 | } 920 | 921 | func (t *Traverser) ExprBinaryGreater(n *ast.ExprBinaryGreater) { 922 | n.Accept(t.v) 923 | 924 | t.Traverse(n.Left) 925 | t.Traverse(n.Right) 926 | } 927 | 928 | func (t *Traverser) ExprBinaryGreaterOrEqual(n *ast.ExprBinaryGreaterOrEqual) { 929 | n.Accept(t.v) 930 | 931 | t.Traverse(n.Left) 932 | t.Traverse(n.Right) 933 | } 934 | 935 | func (t *Traverser) ExprBinaryIdentical(n *ast.ExprBinaryIdentical) { 936 | n.Accept(t.v) 937 | 938 | t.Traverse(n.Left) 939 | t.Traverse(n.Right) 940 | } 941 | 942 | func (t *Traverser) ExprBinaryLogicalAnd(n *ast.ExprBinaryLogicalAnd) { 943 | n.Accept(t.v) 944 | 945 | t.Traverse(n.Left) 946 | t.Traverse(n.Right) 947 | } 948 | 949 | func (t *Traverser) ExprBinaryLogicalOr(n *ast.ExprBinaryLogicalOr) { 950 | n.Accept(t.v) 951 | 952 | t.Traverse(n.Left) 953 | t.Traverse(n.Right) 954 | } 955 | 956 | func (t *Traverser) ExprBinaryLogicalXor(n *ast.ExprBinaryLogicalXor) { 957 | n.Accept(t.v) 958 | 959 | t.Traverse(n.Left) 960 | t.Traverse(n.Right) 961 | } 962 | 963 | func (t *Traverser) ExprBinaryMinus(n *ast.ExprBinaryMinus) { 964 | n.Accept(t.v) 965 | 966 | t.Traverse(n.Left) 967 | t.Traverse(n.Right) 968 | } 969 | 970 | func (t *Traverser) ExprBinaryMod(n *ast.ExprBinaryMod) { 971 | n.Accept(t.v) 972 | 973 | t.Traverse(n.Left) 974 | t.Traverse(n.Right) 975 | } 976 | 977 | func (t *Traverser) ExprBinaryMul(n *ast.ExprBinaryMul) { 978 | n.Accept(t.v) 979 | 980 | t.Traverse(n.Left) 981 | t.Traverse(n.Right) 982 | } 983 | 984 | func (t *Traverser) ExprBinaryNotEqual(n *ast.ExprBinaryNotEqual) { 985 | n.Accept(t.v) 986 | 987 | t.Traverse(n.Left) 988 | t.Traverse(n.Right) 989 | } 990 | 991 | func (t *Traverser) ExprBinaryNotIdentical(n *ast.ExprBinaryNotIdentical) { 992 | n.Accept(t.v) 993 | 994 | t.Traverse(n.Left) 995 | t.Traverse(n.Right) 996 | } 997 | 998 | func (t *Traverser) ExprBinaryPlus(n *ast.ExprBinaryPlus) { 999 | n.Accept(t.v) 1000 | 1001 | t.Traverse(n.Left) 1002 | t.Traverse(n.Right) 1003 | } 1004 | 1005 | func (t *Traverser) ExprBinaryPow(n *ast.ExprBinaryPow) { 1006 | n.Accept(t.v) 1007 | 1008 | t.Traverse(n.Left) 1009 | t.Traverse(n.Right) 1010 | } 1011 | 1012 | func (t *Traverser) ExprBinaryShiftLeft(n *ast.ExprBinaryShiftLeft) { 1013 | n.Accept(t.v) 1014 | 1015 | t.Traverse(n.Left) 1016 | t.Traverse(n.Right) 1017 | } 1018 | 1019 | func (t *Traverser) ExprBinaryShiftRight(n *ast.ExprBinaryShiftRight) { 1020 | n.Accept(t.v) 1021 | 1022 | t.Traverse(n.Left) 1023 | t.Traverse(n.Right) 1024 | } 1025 | 1026 | func (t *Traverser) ExprBinarySmaller(n *ast.ExprBinarySmaller) { 1027 | n.Accept(t.v) 1028 | 1029 | t.Traverse(n.Left) 1030 | t.Traverse(n.Right) 1031 | } 1032 | 1033 | func (t *Traverser) ExprBinarySmallerOrEqual(n *ast.ExprBinarySmallerOrEqual) { 1034 | n.Accept(t.v) 1035 | 1036 | t.Traverse(n.Left) 1037 | t.Traverse(n.Right) 1038 | } 1039 | 1040 | func (t *Traverser) ExprBinarySpaceship(n *ast.ExprBinarySpaceship) { 1041 | n.Accept(t.v) 1042 | 1043 | t.Traverse(n.Left) 1044 | t.Traverse(n.Right) 1045 | } 1046 | 1047 | func (t *Traverser) ExprCastArray(n *ast.ExprCastArray) { 1048 | n.Accept(t.v) 1049 | 1050 | t.Traverse(n.Expr) 1051 | } 1052 | 1053 | func (t *Traverser) ExprCastBool(n *ast.ExprCastBool) { 1054 | n.Accept(t.v) 1055 | 1056 | t.Traverse(n.Expr) 1057 | } 1058 | 1059 | func (t *Traverser) ExprCastDouble(n *ast.ExprCastDouble) { 1060 | n.Accept(t.v) 1061 | 1062 | t.Traverse(n.Expr) 1063 | } 1064 | 1065 | func (t *Traverser) ExprCastInt(n *ast.ExprCastInt) { 1066 | n.Accept(t.v) 1067 | 1068 | t.Traverse(n.Expr) 1069 | } 1070 | 1071 | func (t *Traverser) ExprCastObject(n *ast.ExprCastObject) { 1072 | n.Accept(t.v) 1073 | 1074 | t.Traverse(n.Expr) 1075 | } 1076 | 1077 | func (t *Traverser) ExprCastString(n *ast.ExprCastString) { 1078 | n.Accept(t.v) 1079 | 1080 | t.Traverse(n.Expr) 1081 | } 1082 | 1083 | func (t *Traverser) ExprCastUnset(n *ast.ExprCastUnset) { 1084 | n.Accept(t.v) 1085 | 1086 | t.Traverse(n.Expr) 1087 | } 1088 | 1089 | func (t *Traverser) ScalarDnumber(n *ast.ScalarDnumber) { 1090 | n.Accept(t.v) 1091 | } 1092 | 1093 | func (t *Traverser) ScalarEncapsed(n *ast.ScalarEncapsed) { 1094 | n.Accept(t.v) 1095 | 1096 | for _, nn := range n.Parts { 1097 | nn.Accept(t) 1098 | } 1099 | } 1100 | 1101 | func (t *Traverser) ScalarEncapsedStringPart(n *ast.ScalarEncapsedStringPart) { 1102 | n.Accept(t.v) 1103 | } 1104 | 1105 | func (t *Traverser) ScalarEncapsedStringVar(n *ast.ScalarEncapsedStringVar) { 1106 | n.Accept(t.v) 1107 | 1108 | t.Traverse(n.Name) 1109 | t.Traverse(n.Dim) 1110 | } 1111 | 1112 | func (t *Traverser) ScalarEncapsedStringBrackets(n *ast.ScalarEncapsedStringBrackets) { 1113 | n.Accept(t.v) 1114 | 1115 | t.Traverse(n.Var) 1116 | } 1117 | 1118 | func (t *Traverser) ScalarHeredoc(n *ast.ScalarHeredoc) { 1119 | n.Accept(t.v) 1120 | 1121 | for _, nn := range n.Parts { 1122 | nn.Accept(t) 1123 | } 1124 | } 1125 | 1126 | func (t *Traverser) ScalarLnumber(n *ast.ScalarLnumber) { 1127 | n.Accept(t.v) 1128 | } 1129 | 1130 | func (t *Traverser) ScalarMagicConstant(n *ast.ScalarMagicConstant) { 1131 | n.Accept(t.v) 1132 | } 1133 | 1134 | func (t *Traverser) ScalarString(n *ast.ScalarString) { 1135 | n.Accept(t.v) 1136 | } 1137 | 1138 | func (t *Traverser) NameName(n *ast.Name) { 1139 | n.Accept(t.v) 1140 | 1141 | for _, nn := range n.Parts { 1142 | nn.Accept(t) 1143 | } 1144 | } 1145 | 1146 | func (t *Traverser) NameFullyQualified(n *ast.NameFullyQualified) { 1147 | n.Accept(t.v) 1148 | 1149 | for _, nn := range n.Parts { 1150 | nn.Accept(t) 1151 | } 1152 | } 1153 | 1154 | func (t *Traverser) NameRelative(n *ast.NameRelative) { 1155 | n.Accept(t.v) 1156 | 1157 | for _, nn := range n.Parts { 1158 | nn.Accept(t) 1159 | } 1160 | } 1161 | 1162 | func (t *Traverser) NameNamePart(n *ast.NamePart) { 1163 | n.Accept(t.v) 1164 | } 1165 | --------------------------------------------------------------------------------