Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

注意:

1.打分机制只是一个简单的Demo 实际业务中需要做很多方面的优化
2.使用Golang 进行开发 性能方面需要考虑
3.如果考虑性能要求需要改成C/C++/Rust的方式

参考:

https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l

https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y

基于php-parser 二次开发 php-parser

PHP 代码执行流程图

PHP Parser written in Go

Usage example

package main

import (
	"fmt"
	"log"
	"php-parser/pkg/conf"
	"php-parser/pkg/errors"
	"php-parser/pkg/parser"
	"php-parser/pkg/version"
)

func PHPCheckPars(src string) int {
	var parserErrors []*errors.Error
	errorHandler := func(e *errors.Error) {
		parserErrors = append(parserErrors, e)
	}
	ParseCount, err := parser.Parse([]byte(src), conf.Config{
		Version:          &version.Version{Major: 7, Minor: 0},
		ErrorHandlerFunc: errorHandler,
	})
	if err != nil {
		log.Fatal("Error:" + err.Error())
	}
	if len(parserErrors) > 0 {
		for _, e := range parserErrors {
			log.Println(e.String())
		}
		return 0
	}
	if ParseCount >= 100 {
		fmt.Println("超过100分数拦截")
	} else {
		fmt.Println("无风险")
	}
	return ParseCount
}

func main() {
	src := `
<?php @eval($_POST[1]);
`
	PHPCheckPars(src)
}

一、词法分析

编译
ragel -Z -G2 -o internal/scanner/scanner.go internal/scanner/scanner.l
采用的是ragel 作为词法提取工具
1.1 Lexer结构体 以及实例化

// Lexer 结构体定义了PHP词法分析器的核心数据结构
type Lexer struct {
    data           []byte              // 待分析的源代码字节数组
    phpVersion     *version.Version    // PHP版本信息,用于兼容性处理
    errHandlerFunc func(*errors.Error) // 错误处理函数

    p, pe, cs   int   // p: 当前位置, pe: 数据结束位置, cs: 当前状态
    ts, te, act int   // ts: token开始位置, te: token结束位置, act: 当前动作
    stack       []int // 状态栈,用于处理嵌套结构
    top         int   // 栈顶指针

    heredocLabel []byte         // 当前heredoc标签
    tokenPool    *token.Pool    // token对象池,优化内存分配
    positionPool *position.Pool // 位置对象池,优化内存分配
    newLines     NewLines       // 跟踪源码中的新行位置
}


// NewLexer 创建并初始化一个新的词法分析器实例
func NewLexer(data []byte, config conf.Config) *Lexer {
    lex := &Lexer{
       data:           data,                    // 设置源代码
       phpVersion:     config.Version,          // 设置PHP版本
       errHandlerFunc: config.ErrorHandlerFunc, // 设置错误处理函数

       pe:    len(data),      // 设置数据结束位置
       stack: make([]int, 0), // 初始化状态栈

       // 初始化对象池和行号追踪
       tokenPool:    token.NewPool(position.DefaultBlockSize),
       positionPool: position.NewPool(token.DefaultBlockSize),
       newLines:     NewLines{make([]int, 0, 128)}, // 预分配空间以提高性能
    }
    initLexer(lex) // 初始化词法分析器状态机
    return lex
}

1.1 此法解析状态机 大概的一个状态机的图
PHP Parser written in Go

二、语法分析

编译
goyacc -o internal/php7/php7.go internal/php7/php7.y

参考:
https://github.com/php/php-src/blob/PHP-7.2.32/Zend/zend_language_parser.y
采用yacc 的方式

三、打分

在结构体Parser fraction 是一个分数值

// Parser structure
type Parser struct {
	Lexer          *scanner.Lexer
	currentToken   *token.Token
	rootNode       ast.Vertex
	errHandlerFunc func(*errors.Error)
	builder        *position.Builder
	fraction       int
}


func (p *Parser) GetFraction() int {
return p.fraction
}

func (p *Parser) AddFraction(fraction int) {
p.fraction += fraction
return
}

只需要在php7.y 中调用AddFraction 函数即可
例如

yylex.(*Parser).AddFraction(100)