├── .gitignore ├── README.md ├── ast-names.h ├── astkit-decl.c ├── astkit-list.c ├── astkit-node.c ├── astkit-zval.c ├── astkit.c ├── astkit.h ├── config.m4 ├── config.w32 ├── constants.h ├── php_astkit.h ├── tests ├── bad-graft-00.phpt ├── cache.phpt ├── clone.phpt ├── execute.phpt ├── export │ ├── fragment.phpt │ └── lang │ │ ├── zend_ast_arg_list.phpt │ │ ├── zend_ast_array.phpt │ │ ├── zend_ast_class.phpt │ │ ├── zend_ast_closure.phpt │ │ ├── zend_ast_encaps_list.phpt │ │ ├── zend_ast_func_decl.phpt │ │ ├── zend_ast_if.phpt │ │ ├── zend_ast_method.phpt │ │ ├── zend_ast_switch.phpt │ │ └── zend_ast_zval.phpt ├── graft-echo.phpt ├── graft-if-obj.phpt ├── graft-if.phpt ├── graft-stmt.phpt ├── new.phpt ├── orphan.phpt └── simple-psuedomain.phpt └── util └── astkit-dump.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.lo 2 | *.la 3 | .*.swp 4 | .deps 5 | .libs/ 6 | include/ 7 | tests/*.diff 8 | tests/*.exp 9 | tests/*.log 10 | tests/*.php 11 | tests/*.out 12 | tests/*.sh 13 | Makefile 14 | Makefile.fragments 15 | Makefile.global 16 | Makefile.objects 17 | acinclude.m4 18 | aclocal.m4 19 | autom4te.cache/ 20 | build/ 21 | config.guess 22 | config.h 23 | config.h.in 24 | config.log 25 | config.nice 26 | config.status 27 | config.sub 28 | configure 29 | configure.in 30 | install-sh 31 | libtool 32 | ltmain.sh 33 | missing 34 | mkinstalldirs 35 | modules/ 36 | optimizr.la 37 | optimizr.lo 38 | run-tests.php 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP7 AST exporer 2 | 3 | ### The API 4 | 5 | ``` 6 | class AstKit { 7 | // All ZEND_AST_* enums are registered as constants 8 | const int ZEND_AST_* = *; 9 | 10 | /** 11 | * Option to parseString/parseFile 12 | * 13 | * Disable any zend_ast_hook 14 | * which may have been registered 15 | * by another extension 16 | */ 17 | const int NO_PROCESS = 1; 18 | 19 | /** 20 | * You don't new these objects. 21 | * See AstKit::parseString() and AstKit::parseFile() 22 | * as well as various get*() methods 23 | */ 24 | private function __construct(); 25 | 26 | /** 27 | * Translate a ZEND_AST_* enum to its human readable name 28 | */ 29 | static public function kindName(int $kind): string; 30 | 31 | /** 32 | * Generate an AST from a PHP string 33 | */ 34 | static public function parseString(string $code, 35 | int $options = 0): ?AstKit; 36 | 37 | /** 38 | * Generate an AST from a PHP file 39 | */ 40 | static public function parseFile(string $filename, 41 | int $options = 0): ?AstKit; 42 | 43 | /** 44 | * Graft an AST subtree into position 45 | * @param int $child - Child number to graft onto, existing node will be destroyed 46 | * @param mixed $treeOrValue - AstKit node or ZEND_AST_ZVAL value to emplace 47 | */ 48 | public function graft(int $child, $treeOrValue): void; 49 | 50 | /** 51 | * Execute the current AST fragment 52 | * @return - Result of fragment execution, as though it had been eval()'d 53 | */ 54 | public function execute(): mixed; 55 | 56 | /** 57 | * Get the ZEND_AST_* kind associated with this node 58 | */ 59 | public function getId(): int; 60 | 61 | /** 62 | * Get the attributes for this node 63 | */ 64 | public function getAttributes(): int; 65 | 66 | /** 67 | * Get the line this AST node appeared on 68 | */ 69 | public function getLine(): int; 70 | 71 | /** 72 | * How many children does this node have? 73 | */ 74 | public function numChildren(): int; 75 | 76 | /** 77 | * Get one of this node's children [0, numChildren() - 1] 78 | * @param int $child - Child to fetch 79 | * @param bool $asValue - Whether or not to return simple PHP values (true) or instances of AstKitZval (false) 80 | */ 81 | public function getChild(int $child, bool $asValue = true); 82 | } 83 | 84 | /** 85 | * Specialization of AstKit for List style nodes 86 | * 87 | * The API is the same as AstKit, 88 | * but there are internal differences which make 89 | * having a separate class better 90 | * 91 | * Note that while numChildren/getChild can be used 92 | * Declarations have specific child layouts, 93 | * so the hasX()/getX() may be more useful 94 | * and less brittle. 95 | */ 96 | class AstKitList extends AstKit { 97 | /** 98 | * Create a new, empty ZEND_AST_STMT_LIST node 99 | */ 100 | public function __construct(); 101 | } 102 | 103 | /** 104 | * Specialization of AstKit for declarations 105 | */ 106 | class AstKitDecl extends AstKit { 107 | /** 108 | * Line number on which the declaration ends 109 | */ 110 | public function getLineEnd(): int; 111 | 112 | /** 113 | * Declaration specific flags 114 | */ 115 | public function getFlags(): int; 116 | 117 | /** 118 | * The Doc Comment, duh... 119 | */ 120 | public function getDocComment(): string; 121 | 122 | /** 123 | * The name of the thing being declared 124 | */ 125 | public function getName(): string; 126 | 127 | /** 128 | * Is there a parameter list for this function/method 129 | */ 130 | public function hasParams(): bool; 131 | 132 | /** 133 | * Get the parameter list 134 | */ 135 | public function getParams(): ?AstKit; 136 | 137 | /** 138 | * Are there use variable bound to this closure? 139 | */ 140 | public function hasUse(): bool; 141 | 142 | /** 143 | * Get the use list 144 | */ 145 | public function getUse(): ?AstKit; 146 | 147 | /** 148 | * Are there statements associated with this declaration 149 | */ 150 | public function hasStatements(): bool; 151 | 152 | /** 153 | * Get the statements associated with this declaration 154 | */ 155 | public function getStatements(): ?AstKit; 156 | } 157 | 158 | class AstKitZval extends AstKit { 159 | /** 160 | * Returns the PHP Value for this ZEND_AST_ZVAL node 161 | */ 162 | public function getValue(); 163 | } 164 | ``` 165 | -------------------------------------------------------------------------------- /ast-names.h: -------------------------------------------------------------------------------- 1 | /* special nodes */ 2 | AST(ZVAL) 3 | AST(ZNODE) 4 | 5 | /* declaration nodes */ 6 | AST_DECL(FUNC_DECL) 7 | AST_DECL(CLOSURE) 8 | AST_DECL(METHOD) 9 | AST_DECL(CLASS) 10 | 11 | /* list nodes */ 12 | AST_LIST(ARG_LIST) 13 | #if PHP_VERSION_ID < 70100 14 | AST_LIST(LIST) 15 | #endif 16 | AST_LIST(ARRAY) 17 | AST_LIST(ENCAPS_LIST) 18 | AST_LIST(EXPR_LIST) 19 | AST_LIST(STMT_LIST) 20 | AST_LIST(IF) 21 | AST_LIST(SWITCH_LIST) 22 | AST_LIST(CATCH_LIST) 23 | AST_LIST(PARAM_LIST) 24 | AST_LIST(CLOSURE_USES) 25 | AST_LIST(PROP_DECL) 26 | AST_LIST(CONST_DECL) 27 | AST_LIST(CLASS_CONST_DECL) 28 | AST_LIST(NAME_LIST) 29 | AST_LIST(TRAIT_ADAPTATIONS) 30 | AST_LIST(USE) 31 | 32 | /* 0 child nodes */ 33 | AST_CHILD(MAGIC_CONST, 0) 34 | AST_CHILD(TYPE, 0) 35 | 36 | /* 1 child node */ 37 | AST_CHILD(VAR, 1) 38 | AST_CHILD(CONST, 1) 39 | AST_CHILD(UNPACK, 1) 40 | AST_CHILD(UNARY_PLUS, 1) 41 | AST_CHILD(UNARY_MINUS, 1) 42 | AST_CHILD(CAST, 1) 43 | AST_CHILD(EMPTY, 1) 44 | AST_CHILD(ISSET, 1) 45 | AST_CHILD(SILENCE, 1) 46 | AST_CHILD(SHELL_EXEC, 1) 47 | AST_CHILD(CLONE, 1) 48 | AST_CHILD(EXIT, 1) 49 | AST_CHILD(PRINT, 1) 50 | AST_CHILD(INCLUDE_OR_EVAL, 1) 51 | AST_CHILD(UNARY_OP, 1) 52 | AST_CHILD(PRE_INC, 1) 53 | AST_CHILD(PRE_DEC, 1) 54 | AST_CHILD(POST_INC, 1) 55 | AST_CHILD(POST_DEC, 1) 56 | AST_CHILD(YIELD_FROM, 1) 57 | 58 | AST_CHILD(GLOBAL, 1) 59 | AST_CHILD(UNSET, 1) 60 | AST_CHILD(RETURN, 1) 61 | AST_CHILD(LABEL, 1) 62 | AST_CHILD(REF, 1) 63 | AST_CHILD(HALT_COMPILER, 1) 64 | AST_CHILD(ECHO, 1) 65 | AST_CHILD(THROW, 1) 66 | AST_CHILD(GOTO, 1) 67 | AST_CHILD(BREAK, 1) 68 | AST_CHILD(CONTINUE, 1) 69 | 70 | /* 2 child nodes */ 71 | AST_CHILD(DIM, 2) 72 | AST_CHILD(PROP, 2) 73 | AST_CHILD(STATIC_PROP, 2) 74 | AST_CHILD(CALL, 2) 75 | AST_CHILD(CLASS_CONST, 2) 76 | AST_CHILD(ASSIGN, 2) 77 | AST_CHILD(ASSIGN_REF, 2) 78 | AST_CHILD(ASSIGN_OP, 2) 79 | AST_CHILD(BINARY_OP, 2) 80 | AST_CHILD(GREATER, 2) 81 | AST_CHILD(GREATER_EQUAL, 2) 82 | AST_CHILD(AND, 2) 83 | AST_CHILD(OR, 2) 84 | AST_CHILD(ARRAY_ELEM, 2) 85 | AST_CHILD(NEW, 2) 86 | AST_CHILD(INSTANCEOF, 2) 87 | AST_CHILD(YIELD, 2) 88 | AST_CHILD(COALESCE, 2) 89 | 90 | AST_CHILD(STATIC, 2) 91 | AST_CHILD(WHILE, 2) 92 | AST_CHILD(DO_WHILE, 2) 93 | AST_CHILD(IF_ELEM, 2) 94 | AST_CHILD(SWITCH, 2) 95 | AST_CHILD(SWITCH_CASE, 2) 96 | AST_CHILD(DECLARE, 2) 97 | #if PHP_VERSION_ID < 70100 98 | AST_CHILD(CONST_ELEM, 2) 99 | #endif 100 | AST_CHILD(USE_TRAIT, 2) 101 | AST_CHILD(TRAIT_PRECEDENCE, 2) 102 | AST_CHILD(METHOD_REFERENCE, 2) 103 | AST_CHILD(NAMESPACE, 2) 104 | AST_CHILD(USE_ELEM, 2) 105 | AST_CHILD(TRAIT_ALIAS, 2) 106 | AST_CHILD(GROUP_USE, 2) 107 | 108 | /* 3 child nodes */ 109 | AST_CHILD(METHOD_CALL, 3) 110 | AST_CHILD(STATIC_CALL, 3) 111 | AST_CHILD(CONDITIONAL, 3) 112 | 113 | AST_CHILD(TRY, 3) 114 | AST_CHILD(CATCH, 3) 115 | AST_CHILD(PARAM, 3) 116 | AST_CHILD(PROP_ELEM, 3) 117 | #if PHP_VERSION_ID >= 70100 118 | AST_CHILD(CONST_ELEM, 3) 119 | #endif 120 | 121 | /* 4 child nodes */ 122 | AST_CHILD(FOR, 4) 123 | AST_CHILD(FOREACH, 4) 124 | -------------------------------------------------------------------------------- /astkit-decl.c: -------------------------------------------------------------------------------- 1 | #include "astkit.h" 2 | 3 | zend_class_entry* astkit_decl_ce = NULL; 4 | 5 | /* {{{ proto int AstKitDecl::numChildren() */ 6 | static PHP_METHOD(AstKitDecl, numChildren) { 7 | RETURN_LONG(3); 8 | } /* }}} */ 9 | 10 | /* {{{ proto object AstKitDecl::getChild(int child[, bool zval_as_value = true]) */ 11 | ZEND_BEGIN_ARG_INFO(AstKitDecl_getChild_arginfo, 0) 12 | ZEND_ARG_INFO(0, child) 13 | ZEND_ARG_INFO(0, zval_as_value) 14 | ZEND_END_ARG_INFO() 15 | static PHP_METHOD(AstKitDecl, getChild) { 16 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 17 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 18 | zend_long child; 19 | zend_bool zval_as_value = 1; 20 | 21 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &child, &zval_as_value) == FAILURE) { 22 | return; 23 | } 24 | 25 | if ((child < 0) || (child > 3)) { 26 | php_error_docref(NULL, E_WARNING, 27 | "Invalid child " ZEND_LONG_FMT ", current node has %d children", 28 | child, 3); 29 | return; 30 | } 31 | if (declNode->child[child]) { 32 | astkit_create_object(return_value, declNode->child[child], objval->tree, zval_as_value); 33 | } 34 | } /* }}} */ 35 | 36 | /* {{{ proto int AstKitDecl::getLineEnd() */ 37 | static PHP_METHOD(AstKitDecl, getLineEnd) { 38 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 39 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 40 | RETURN_LONG(declNode->end_lineno); 41 | } /* }}} */ 42 | 43 | /* {{{ proto int AstKitDecl::geFlags() */ 44 | static PHP_METHOD(AstKitDecl, getFlags) { 45 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 46 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 47 | RETURN_LONG(declNode->flags); 48 | } /* }}} */ 49 | 50 | /* {{{ proto string AstKitDecl::getDocComment() */ 51 | static PHP_METHOD(AstKitDecl, getDocComment) { 52 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 53 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 54 | if (declNode->doc_comment) { 55 | RETURN_STRINGL(declNode->doc_comment->val, declNode->doc_comment->len); 56 | } else { 57 | RETURN_EMPTY_STRING(); 58 | } 59 | } /* }}} */ 60 | 61 | /* {{{ proto string AstKitDecl::getName() */ 62 | static PHP_METHOD(AstKitDecl, getName) { 63 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 64 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 65 | RETURN_STRINGL(declNode->name->val, declNode->name->len); 66 | } /* }}} */ 67 | 68 | /* {{{ proto bool AstKitDecl::hasParams() */ 69 | static PHP_METHOD(AstKitDecl, hasParams) { 70 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 71 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 72 | RETURN_BOOL(declNode->child[0]); 73 | } /* }}} */ 74 | 75 | /* {{{ proto mixed AstKitDecl::getParams() */ 76 | static PHP_METHOD(AstKitDecl, getParams) { 77 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 78 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 79 | astkit_create_object(return_value, declNode->child[0], objval->tree, 0); 80 | } /* }}} */ 81 | 82 | /* {{{ proto bool AstKitDecl::hasUse() */ 83 | static PHP_METHOD(AstKitDecl, hasUse) { 84 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 85 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 86 | RETURN_BOOL(declNode->child[1]); 87 | } /* }}} */ 88 | 89 | /* {{{ proto mixed AstKitDecl::getUse() */ 90 | static PHP_METHOD(AstKitDecl, getUse) { 91 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 92 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 93 | astkit_create_object(return_value, declNode->child[1], objval->tree, 0); 94 | } /* }}} */ 95 | 96 | /* {{{ proto bool AstKitDecl::hasStatements() */ 97 | static PHP_METHOD(AstKitDecl, hasStatements) { 98 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 99 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 100 | RETURN_BOOL(declNode->child[2]); 101 | } /* }}} */ 102 | 103 | /* {{{ proto mixed AstKitDecl::getStatements() */ 104 | static PHP_METHOD(AstKitDecl, getStatements) { 105 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 106 | zend_ast_decl* declNode = (zend_ast_decl*)objval->node; 107 | astkit_create_object(return_value, declNode->child[2], objval->tree, 0); 108 | } /* }}} */ 109 | 110 | static zend_function_entry astkit_decl_methods[] = { 111 | /* For compat with AstKit, but the hasFoo()/getFoo() APIs are more appropriate */ 112 | PHP_ME(AstKitDecl, numChildren, NULL, ZEND_ACC_PUBLIC) 113 | PHP_ME(AstKitDecl, getChild, AstKitDecl_getChild_arginfo, ZEND_ACC_PUBLIC) 114 | 115 | PHP_ME(AstKitDecl, getLineEnd, NULL, ZEND_ACC_PUBLIC) 116 | PHP_ME(AstKitDecl, getFlags, NULL, ZEND_ACC_PUBLIC) 117 | PHP_ME(AstKitDecl, getDocComment, NULL, ZEND_ACC_PUBLIC) 118 | PHP_ME(AstKitDecl, getName, NULL, ZEND_ACC_PUBLIC) 119 | 120 | PHP_ME(AstKitDecl, hasParams, NULL, ZEND_ACC_PUBLIC) 121 | PHP_ME(AstKitDecl, getParams, NULL, ZEND_ACC_PUBLIC) 122 | PHP_ME(AstKitDecl, hasUse, NULL, ZEND_ACC_PUBLIC) 123 | PHP_ME(AstKitDecl, getUse, NULL, ZEND_ACC_PUBLIC) 124 | PHP_ME(AstKitDecl, hasStatements, NULL, ZEND_ACC_PUBLIC) 125 | PHP_ME(AstKitDecl, getStatements, NULL, ZEND_ACC_PUBLIC) 126 | /* getLineEnd() */ 127 | PHP_FE_END 128 | }; 129 | 130 | int astkit_decl_minit(INIT_FUNC_ARGS) { 131 | zend_class_entry ce; 132 | 133 | INIT_CLASS_ENTRY(ce, "AstKitDecl", astkit_decl_methods); 134 | astkit_decl_ce = zend_register_internal_class_ex(&ce, astkit_node_ce); 135 | 136 | return SUCCESS; 137 | } 138 | -------------------------------------------------------------------------------- /astkit-list.c: -------------------------------------------------------------------------------- 1 | #include "astkit.h" 2 | 3 | zend_class_entry* astkit_list_ce = NULL; 4 | 5 | /* {{{ proto void AstKitList::__construct() */ 6 | static PHP_METHOD(AstKitList, __construct) { 7 | zval *thz = getThis(); 8 | astkit_object* objval = ASTKIT_FETCH_OBJ(thz); 9 | zend_arena* old_arena = CG(ast_arena); 10 | 11 | CG(ast_arena) = zend_arena_create(32 * 1024); 12 | objval->node = zend_ast_create_list(0, ZEND_AST_STMT_LIST); 13 | objval->tree = emalloc(sizeof(astkit_tree)); 14 | objval->tree->root = objval->node; 15 | objval->tree->arena = CG(ast_arena); 16 | objval->tree->refcount = 1; 17 | 18 | CG(ast_arena) = old_arena; 19 | zend_hash_index_add(&ASTKITG(cache), (zend_ulong)objval->node, thz); 20 | } /* }}} */ 21 | 22 | /* {{{ proto int AstKitList::numChildren() */ 23 | static PHP_METHOD(AstKitList, numChildren) { 24 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 25 | zend_ast_list* node = (zend_ast_list*)objval->node; 26 | RETURN_LONG(node->children); 27 | } /* }}} */ 28 | 29 | /* {{{ proto object AstKitList::getChild(int child[, bool zval_as_value = true]) */ 30 | ZEND_BEGIN_ARG_INFO(AstKitList_getChild_arginfo, 0) 31 | ZEND_ARG_INFO(0, child) 32 | ZEND_ARG_INFO(0, zval_as_value) 33 | ZEND_END_ARG_INFO() 34 | static PHP_METHOD(AstKitList, getChild) { 35 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 36 | zend_ast_list* listNode = (zend_ast_list*)objval->node; 37 | zend_long child; 38 | zend_bool zval_as_value = 1; 39 | 40 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &child, &zval_as_value) == FAILURE) { 41 | return; 42 | } 43 | 44 | if ((child < 0) || (child >= ((zend_long)(listNode->children)))) { 45 | php_error_docref(NULL, E_WARNING, 46 | "Invalid child " ZEND_LONG_FMT ", current node has %d children", 47 | child, listNode->children); 48 | return; 49 | } 50 | astkit_create_object(return_value, listNode->child[child], objval->tree, zval_as_value); 51 | } /* }}} */ 52 | 53 | /* {{{ proto void AstKitList::apend(AstKit|mixed $node) */ 54 | ZEND_BEGIN_ARG_INFO(AstKitList_append_arginfo, 0) 55 | ZEND_ARG_INFO(0, node) 56 | ZEND_END_ARG_INFO() 57 | static PHP_METHOD(AstKitList, append) { 58 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 59 | zend_ast_list* listNode = (zend_ast_list*)objval->node; 60 | zval *node; 61 | 62 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &node) == FAILURE) { 63 | return; 64 | } 65 | 66 | astkit_graft(objval, listNode->children, node); 67 | } /* }}} */ 68 | 69 | 70 | static zend_function_entry astkit_list_methods[] = { 71 | PHP_ME(AstKitList, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 72 | PHP_ME(AstKitList, numChildren, NULL, ZEND_ACC_PUBLIC) 73 | PHP_ME(AstKitList, getChild, AstKitList_getChild_arginfo, ZEND_ACC_PUBLIC) 74 | PHP_ME(AstKitList, append, AstKitList_append_arginfo, ZEND_ACC_PUBLIC) 75 | PHP_FE_END 76 | }; 77 | 78 | int astkit_list_minit(INIT_FUNC_ARGS) { 79 | zend_class_entry ce; 80 | 81 | INIT_CLASS_ENTRY(ce, "AstKitList", astkit_list_methods); 82 | astkit_list_ce = zend_register_internal_class_ex(&ce, astkit_node_ce); 83 | 84 | return SUCCESS; 85 | } 86 | -------------------------------------------------------------------------------- /astkit-node.c: -------------------------------------------------------------------------------- 1 | #include "astkit.h" 2 | #include "zend_language_scanner.h" 3 | #include "zend_language_parser.h" 4 | 5 | #define ASTKIT_NO_PROCESS 1 6 | 7 | zend_class_entry* astkit_node_ce = NULL; 8 | static zend_object_handlers astkit_object_handlers; 9 | static void astkit_ast_destroy(zend_ast* ast); 10 | 11 | /* {{{ proto string AstKit::kindName(int $kind) */ 12 | ZEND_BEGIN_ARG_INFO(AstKind_kindName_arginfo, 0) 13 | ZEND_ARG_INFO(0, kind) 14 | ZEND_END_ARG_INFO(); 15 | static PHP_METHOD(AstKit, kindName) { 16 | zend_long kind; 17 | 18 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &kind) == FAILURE) { 19 | return; 20 | } 21 | 22 | switch (kind) { 23 | #define AST(id) case ZEND_AST_##id: RETURN_STRING("ZEND_AST_" #id); 24 | #define AST_DECL(id) AST(id) 25 | #define AST_LIST(id) AST(id) 26 | #define AST_CHILD(id, children) AST(id) 27 | #include "ast-names.h" 28 | #undef AST_CHILD 29 | #undef AST_LIST 30 | #undef AST_DECL 31 | #undef AST 32 | default: 33 | php_error_docref(NULL, E_WARNING, "Unknown AST kind: " ZEND_LONG_FMT, kind); 34 | RETURN_NULL(); 35 | } 36 | } /* }}} */ 37 | 38 | /* {{{ proto AstKitList AstKit::parseString(string $code) */ 39 | ZEND_BEGIN_ARG_INFO(AstKind_parseString_arginfo, 0) 40 | ZEND_ARG_INFO(0, code) 41 | ZEND_ARG_INFO(0, options) 42 | ZEND_END_ARG_INFO() 43 | static PHP_METHOD(AstKit, parseString) { 44 | zend_string* source_string; 45 | zend_long options = 0; 46 | zval source; 47 | zend_lex_state original_lex_state; 48 | astkit_tree *tree; 49 | int parse_result; 50 | 51 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &source_string, &options) == FAILURE) { 52 | return; 53 | } 54 | 55 | zend_save_lexical_state(&original_lex_state); 56 | ZVAL_STR_COPY(&source, source_string); 57 | if (zend_prepare_string_for_scanning(&source, "AstKit::parseString()") == FAILURE) { 58 | zend_restore_lexical_state(&original_lex_state); 59 | RETURN_FALSE; 60 | } 61 | 62 | CG(ast) = NULL; 63 | CG(ast_arena) = zend_arena_create(1024 * 32); 64 | parse_result = zendparse(); 65 | zval_dtor(&source); 66 | if (parse_result != 0) { 67 | /* parse error */ 68 | zend_ast_destroy(CG(ast)); 69 | zend_arena_destroy(CG(ast_arena)); 70 | CG(ast) = NULL; 71 | CG(ast_arena) = NULL; 72 | zend_restore_lexical_state(&original_lex_state); 73 | RETURN_FALSE; 74 | } 75 | 76 | if (zend_ast_process && 77 | !(options & ASTKIT_NO_PROCESS)) { 78 | zend_ast_process(CG(ast)); 79 | } 80 | tree = emalloc(sizeof(astkit_tree)); 81 | tree->root = CG(ast); 82 | tree->arena = CG(ast_arena); 83 | tree->refcount = 0; 84 | 85 | CG(ast) = NULL; 86 | CG(ast_arena) = NULL; 87 | zend_restore_lexical_state(&original_lex_state); 88 | astkit_create_object(return_value, tree->root, tree, 0); 89 | } /* }}} */ 90 | 91 | /* {{{ proto AstKitList AstKit::parseFile(string $filename) */ 92 | ZEND_BEGIN_ARG_INFO(AstKind_parseFile_arginfo, 0) 93 | ZEND_ARG_INFO(0, filename) 94 | ZEND_ARG_INFO(0, options) 95 | ZEND_END_ARG_INFO() 96 | static PHP_METHOD(AstKit, parseFile) { 97 | char *filename; 98 | size_t filename_len; 99 | zend_long options = 0; 100 | zend_file_handle file_handle; 101 | zend_lex_state original_lex_state; 102 | astkit_tree *tree; 103 | int parse_result; 104 | 105 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &filename, &filename_len, &options) == FAILURE) { 106 | return; 107 | } 108 | 109 | if (FAILURE == zend_stream_open(filename, &file_handle)) { 110 | /* TODO: throw an error? */ 111 | return; 112 | } 113 | if (open_file_for_scanning(&file_handle)==FAILURE) { 114 | zend_destroy_file_handle(&file_handle); 115 | return; 116 | } 117 | 118 | CG(ast) = NULL; 119 | CG(ast_arena) = zend_arena_create(1024 * 32); 120 | zend_save_lexical_state(&original_lex_state); 121 | parse_result = zendparse(); 122 | if (parse_result != 0) { 123 | /* parse error */ 124 | zend_ast_destroy(CG(ast)); 125 | zend_arena_destroy(CG(ast_arena)); 126 | zend_restore_lexical_state(&original_lex_state); 127 | zend_destroy_file_handle(&file_handle); 128 | RETURN_FALSE; 129 | } 130 | 131 | if (zend_ast_process && 132 | !(options & ASTKIT_NO_PROCESS)) { 133 | zend_ast_process(CG(ast)); 134 | } 135 | 136 | tree = emalloc(sizeof(astkit_tree)); 137 | tree->root = CG(ast); 138 | tree->arena = CG(ast_arena); 139 | tree->refcount = 0; 140 | 141 | CG(ast) = NULL; 142 | CG(ast_arena) = NULL; 143 | zend_restore_lexical_state(&original_lex_state); 144 | zend_destroy_file_handle(&file_handle); 145 | astkit_create_object(return_value, tree->root, tree, 0); 146 | } /* }}} */ 147 | 148 | /* {{{ proto void AstKit::__construct() */ 149 | static PHP_METHOD(AstKit, __construct) { 150 | /* Never called */ 151 | php_error_docref(NULL, E_ERROR, "AstKit::__construct is private"); 152 | } /* }}} */ 153 | 154 | /* {{{ proto int AstKit::getId() */ 155 | static PHP_METHOD(AstKit, getId) { 156 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 157 | RETURN_LONG(objval->node->kind); 158 | } /* }}} */ 159 | 160 | /* {{{ proto int AstKit::getAttributes() */ 161 | static PHP_METHOD(AstKit, getAttributes) { 162 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 163 | RETURN_LONG(objval->node->attr); 164 | } /* }}} */ 165 | 166 | /* {{{ proto int AstKit::getLine() */ 167 | static PHP_METHOD(AstKit, getLine) { 168 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 169 | RETURN_LONG(objval->node->lineno); 170 | } /* }}} */ 171 | 172 | /* {{{ proto int AstKit::numChildren() */ 173 | static PHP_METHOD(AstKit, numChildren) { 174 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 175 | 176 | RETURN_LONG(objval->node->kind >> ZEND_AST_NUM_CHILDREN_SHIFT); 177 | } /* }}} */ 178 | 179 | /* {{{ proto mixed AstKit::getChild(int $child[, bool $zval_as_value = true]) */ 180 | ZEND_BEGIN_ARG_INFO(AstKit_getChild_arginfo, 0) 181 | ZEND_ARG_INFO(0, child) 182 | ZEND_ARG_INFO(0, zval_as_value) 183 | ZEND_END_ARG_INFO() 184 | static PHP_METHOD(AstKit, getChild) { 185 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 186 | int numChildren = objval->node->kind >> ZEND_AST_NUM_CHILDREN_SHIFT; 187 | zend_long child; 188 | zend_bool zval_as_value = 1; 189 | 190 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &child, &zval_as_value) == FAILURE) { 191 | return; 192 | } 193 | 194 | if ((child < 0) || (child >= numChildren)) { 195 | php_error_docref(NULL, E_WARNING, 196 | "Invalid child " ZEND_LONG_FMT ", current node has %d children", 197 | child, numChildren); 198 | return; 199 | } 200 | 201 | astkit_create_object(return_value, objval->node->child[child], objval->tree, zval_as_value); 202 | } /* }}} */ 203 | 204 | /* {{{ proto string AstKit::export() */ 205 | static PHP_METHOD(AstKit, export) { 206 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 207 | zend_string *ret = zend_ast_export("", objval->node, ""); 208 | 209 | if (ret) { 210 | RETURN_STR(ret); 211 | } else { 212 | RETURN_NULL(); 213 | } 214 | } /* }}} */ 215 | 216 | /* {{{ proto void AstKit::graft(int $child, mixed $treeOrValue) */ 217 | ZEND_BEGIN_ARG_INFO(AstKit_graft_arginfo, 0) 218 | ZEND_ARG_INFO(0, child) 219 | ZEND_ARG_INFO(0, treeOrValue) 220 | ZEND_END_ARG_INFO(); 221 | static PHP_METHOD(AstKit, graft) { 222 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 223 | zend_long child; 224 | zval *value; 225 | 226 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &child, &value) == FAILURE) { 227 | return; 228 | } 229 | 230 | astkit_graft(objval, child, value); 231 | } /* }}} */ 232 | 233 | /* {{{ astkit_graft */ 234 | void astkit_graft(astkit_object* objval, int child, zval* value) { 235 | zend_arena *old_arena; 236 | zend_ast *new_child; 237 | 238 | if (child < 0) { 239 | php_error_docref(NULL, E_WARNING, "Invalid child number, must be positive integer"); 240 | return; 241 | } 242 | 243 | old_arena = CG(ast_arena); 244 | CG(ast_arena) = objval->tree->arena; 245 | 246 | if ((Z_TYPE_P(value) == IS_OBJECT) && 247 | instanceof_function(Z_OBJCE_P(value), astkit_node_ce)) { 248 | new_child = astkit_ast_copy(ASTKIT_FETCH_OBJ(value)->node); 249 | } else if (Z_TYPE_P(value) != IS_OBJECT) { 250 | new_child = zend_ast_create_zval(value); 251 | } else { 252 | php_error_docref(NULL, E_WARNING, "Unable t graft objects into AST nodes"); 253 | CG(ast_arena) = old_arena; 254 | return; 255 | } 256 | CG(ast_arena) = old_arena; 257 | 258 | if ((objval->node->kind >> ZEND_AST_SPECIAL_SHIFT) == 1) { 259 | /* Don't allow grafting decls, it gets... weird. There are better ways, honey. */ 260 | php_error_docref(NULL, E_WARNING, "Unable to graft onto Decl nodes directly."); 261 | return; 262 | } else if ((objval->node->kind >> ZEND_AST_IS_LIST_SHIFT) == 1) { 263 | zend_ast_list *list = zend_ast_get_list(objval->node); 264 | zend_bool append = child >= list->children; 265 | if (append) { 266 | /* Append new child */ 267 | zend_ast_list_add(objval->node, new_child); 268 | } else { 269 | /* Replace existing child */ 270 | old_arena = CG(ast_arena); 271 | CG(ast_arena) = objval->tree->arena; 272 | astkit_ast_destroy(list->child[child]); 273 | CG(ast_arena) = old_arena; 274 | 275 | list->child[child] = new_child; 276 | } 277 | } else { 278 | int numChildren = objval->node->kind >> ZEND_AST_NUM_CHILDREN_SHIFT; 279 | if (child >= numChildren) { 280 | zend_ast_destroy(new_child); 281 | php_error_docref(NULL, E_WARNING, "Node only has %d child slots, cannot graft onto child %d", 282 | numChildren, child); 283 | return; 284 | } 285 | 286 | old_arena = CG(ast_arena); 287 | CG(ast_arena) = objval->tree->arena; 288 | astkit_ast_destroy(objval->node->child[child]); 289 | CG(ast_arena) = old_arena; 290 | 291 | objval->node->child[child] = new_child; 292 | } 293 | } /* }}} */ 294 | 295 | /* {{{ proto mixed AstKit::execute() */ 296 | static PHP_METHOD(AstKit, execute) { 297 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 298 | 299 | zend_arena *old_arena = CG(ast_arena); 300 | 301 | ASTKITG(hijack_ast_arena) = CG(ast_arena) = zend_arena_create(32 * 1024); 302 | ASTKITG(hijack_ast) = astkit_ast_copy(objval->node); 303 | 304 | CG(ast_arena) = old_arena; 305 | 306 | char *eval_name = zend_make_compiled_string_description("AstKit::execute"); 307 | zend_eval_string("", return_value, eval_name); 308 | efree(eval_name); 309 | } /* }}} */ 310 | 311 | static zend_function_entry astkit_node_methods[] = { 312 | PHP_ME(AstKit, kindName, AstKind_kindName_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 313 | PHP_ME(AstKit, parseString, AstKind_parseString_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 314 | PHP_ME(AstKit, parseFile, AstKind_parseFile_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 315 | 316 | PHP_ME(AstKit, __construct, NULL, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR) 317 | PHP_ME(AstKit, getId, NULL, ZEND_ACC_PUBLIC) 318 | PHP_ME(AstKit, getAttributes, NULL, ZEND_ACC_PUBLIC) 319 | PHP_ME(AstKit, getLine, NULL, ZEND_ACC_PUBLIC) 320 | PHP_ME(AstKit, numChildren, NULL, ZEND_ACC_PUBLIC) 321 | PHP_ME(AstKit, getChild, AstKit_getChild_arginfo, ZEND_ACC_PUBLIC) 322 | 323 | PHP_ME(AstKit, export, NULL, ZEND_ACC_PUBLIC) 324 | PHP_ME(AstKit, graft, AstKit_graft_arginfo, ZEND_ACC_PUBLIC) 325 | PHP_ME(AstKit, execute, NULL, ZEND_ACC_PUBLIC) 326 | PHP_FE_END 327 | }; 328 | 329 | /* Can't use zend_ast_copy() because it uses emalloc instead of zend_arena_alloc 330 | * and it doesn't handle copying declarations. 331 | */ 332 | static inline size_t zend_ast_size(uint32_t children) { 333 | return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children; 334 | } 335 | static inline size_t zend_ast_list_size(uint32_t children) { 336 | return sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * children; 337 | } 338 | zend_ast* astkit_ast_copy(zend_ast* ast) { 339 | if (ast == NULL) { 340 | return NULL; 341 | } else if (ast->kind == ZEND_AST_ZVAL) { 342 | zend_ast_zval *new = zend_arena_alloc(&CG(ast_arena), sizeof(zend_ast_zval)); 343 | *new = *((zend_ast_zval*)ast); 344 | zval_copy_ctor(&new->val); 345 | return (zend_ast*)new; 346 | } else if (ast->kind == ZEND_AST_ZNODE) { 347 | php_error_docref(NULL, E_WARNING, "Encountered unexpected AST_ZNODE"); 348 | return NULL; 349 | } else if (zend_ast_is_list(ast)) { 350 | zend_ast_list *list = zend_ast_get_list(ast); 351 | zend_ast_list *new = zend_arena_alloc(&CG(ast_arena), zend_ast_list_size(list->children)); 352 | uint32_t i; 353 | *new = *list; 354 | for (i = 0; i < new->children; ++i) { 355 | new->child[i] = astkit_ast_copy(list->child[i]); 356 | } 357 | return (zend_ast*)new; 358 | } else if (ast->kind < (1 << ZEND_AST_IS_LIST_SHIFT)) { 359 | uint32_t i; 360 | zend_ast_decl *decl = (zend_ast_decl*)ast; 361 | zend_ast_decl *new = zend_arena_alloc(&CG(ast_arena), sizeof(zend_ast_decl)); 362 | *new = *decl; 363 | if (new->doc_comment) zend_string_addref(new->doc_comment); 364 | if (new->name) zend_string_addref(new->name); 365 | for (i = 0; i < 4; i++) { 366 | new->child[i] = astkit_ast_copy(decl->child[i]); 367 | } 368 | return (zend_ast*)new; 369 | } else { 370 | uint32_t i, children = zend_ast_get_num_children(ast); 371 | zend_ast *new = zend_arena_alloc(&CG(ast_arena), zend_ast_size(children)); 372 | *new = *ast; 373 | for (i = 0; i < children; i++) { 374 | new->child[i] = astkit_ast_copy(ast->child[i]); 375 | } 376 | return new; 377 | } 378 | } 379 | 380 | static void astkit_ast_destroy(zend_ast* ast) { 381 | zval *obj; 382 | if (!ast) return; 383 | obj = zend_hash_index_find(&ASTKITG(cache), (zend_ulong)ast); 384 | if (obj) { 385 | /* Owned by an object somewhere, mark it as orphaned from the root */ 386 | zval undef; 387 | ZVAL_UNDEF(&undef); 388 | zend_hash_index_add(&ASTKITG(orphan), (zend_ulong)ast, &undef); 389 | return; 390 | } 391 | 392 | if (ast->kind == ZEND_AST_ZVAL) { 393 | /* No children to scan through */ 394 | } else if (ast->kind == ZEND_AST_ZNODE) { 395 | php_error_docref(NULL, E_WARNING, "Encountered unexpected AST_ZNODE"); 396 | } else if (zend_ast_is_list(ast)) { 397 | zend_ast_list *list = zend_ast_get_list(ast); 398 | uint32_t i; 399 | for (i = 0; i < list->children; ++i) { 400 | astkit_ast_destroy(list->child[i]); 401 | } 402 | list->children = 0; 403 | } else if (ast->kind < (1 << ZEND_AST_IS_LIST_SHIFT)) { 404 | uint32_t i; 405 | zend_ast_decl *decl = (zend_ast_decl*)ast; 406 | for (i = 0; i < 4; i++) { 407 | astkit_ast_destroy(decl->child[i]); 408 | decl->child[i] = NULL; 409 | } 410 | } else { 411 | uint32_t i, children = zend_ast_get_num_children(ast); 412 | for (i = 0; i < children; i++) { 413 | astkit_ast_destroy(ast->child[i]); 414 | ast->child[i] = NULL; 415 | } 416 | } 417 | zend_ast_destroy(ast); 418 | } 419 | 420 | static zend_object* astkit_node_create(zend_class_entry* ce) { 421 | astkit_object* object = ecalloc(1, sizeof(astkit_object) + zend_object_properties_size(ce)); 422 | zend_object* ret = astkit_to_zend_object(object); 423 | zend_object_std_init(ret, ce); 424 | ret->handlers = &astkit_object_handlers; 425 | object_properties_init(ret, ce); 426 | return ret; 427 | } 428 | 429 | static zend_object* astkit_node_clone(zval *srcObj) { 430 | zend_arena *old_arena = CG(ast_arena); 431 | astkit_object* src = ASTKIT_FETCH_OBJ(srcObj); 432 | astkit_object* dest = astkit_from_zend_object(astkit_node_create(Z_OBJCE_P(srcObj))); 433 | zend_object* ret = astkit_to_zend_object(dest); 434 | zend_objects_clone_members(ret, astkit_to_zend_object(src)); 435 | 436 | dest->tree = emalloc(sizeof(astkit_tree)); 437 | dest->tree->arena = CG(ast_arena) = zend_arena_create(1024 * 32); 438 | dest->tree->refcount = 1; 439 | dest->tree->root = dest->node = astkit_ast_copy(src->node); 440 | 441 | CG(ast_arena) = old_arena; 442 | return ret; 443 | } 444 | 445 | static void astkit_node_free(zend_object* obj) { 446 | astkit_object* object = astkit_from_zend_object(obj); 447 | zend_hash_index_del(&ASTKITG(cache), (zend_ulong)object->node); 448 | if (zend_hash_index_exists(&ASTKITG(orphan), (zend_ulong)object->node)) { 449 | astkit_ast_destroy(object->node); 450 | zend_hash_index_del(&ASTKITG(orphan), (zend_ulong)object->node); 451 | } 452 | if (object->tree) { 453 | if ((--object->tree->refcount) <= 0) { 454 | zend_ast_destroy(object->tree->root); 455 | zend_arena_destroy(object->tree->arena); 456 | efree(object->tree); 457 | } 458 | } 459 | zend_object_std_dtor(obj); 460 | } 461 | 462 | static zend_op_array *(*old_compile_string)(zval*, char*); 463 | static zend_op_array *astkit_compile_string(zval* source_string, char* filename) { 464 | if (!ASTKITG(hijack_ast)) { 465 | return old_compile_string(source_string, filename); 466 | } 467 | 468 | /* Hijack in progress, bypass any opcache */ 469 | return compile_string(source_string, filename); 470 | } 471 | 472 | static void (*old_ast_process)(zend_ast*); 473 | static void astkit_ast_process(zend_ast* ast) { 474 | ZEND_ASSERT(ast == CG(ast)); 475 | if (ASTKITG(hijack_ast)) { 476 | /* Destroy the dummy ast, swap in ours, and release it */ 477 | zend_ast_destroy(CG(ast)); 478 | zend_arena_destroy(CG(ast_arena)); 479 | 480 | CG(ast) = ASTKITG(hijack_ast); 481 | CG(ast_arena) = ASTKITG(hijack_ast_arena); 482 | 483 | ASTKITG(hijack_ast) = NULL; 484 | ASTKITG(hijack_ast_arena) = NULL; 485 | } 486 | if (old_ast_process) { 487 | old_ast_process(CG(ast)); 488 | } 489 | } 490 | 491 | int astkit_node_minit(INIT_FUNC_ARGS) { 492 | zend_class_entry ce; 493 | 494 | memcpy(&astkit_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 495 | astkit_object_handlers.offset = XtOffsetOf(astkit_object, std); 496 | astkit_object_handlers.free_obj = astkit_node_free; 497 | astkit_object_handlers.clone_obj = astkit_node_clone; 498 | 499 | INIT_CLASS_ENTRY(ce, "AstKit", astkit_node_methods); 500 | astkit_node_ce = zend_register_internal_class(&ce); 501 | astkit_node_ce->create_object = astkit_node_create; 502 | 503 | zend_declare_class_constant_long(astkit_node_ce, "NO_PROCESS", sizeof("NO_PROCESS") - 1, ASTKIT_NO_PROCESS); 504 | 505 | #define AST(id) zend_declare_class_constant_long(astkit_node_ce, "ZEND_AST_" #id, sizeof("ZEND_AST_" #id) - 1, ZEND_AST_##id); 506 | #define AST_DECL(id) AST(id) 507 | #define AST_LIST(id) AST(id) 508 | #define AST_CHILD(id, children) AST(id) 509 | #include "ast-names.h" 510 | #undef AST_CHILD 511 | #undef AST_LIST 512 | #undef AST_DECL 513 | #undef AST 514 | 515 | #define AST_CONST(id) zend_declare_class_constant_long(astkit_node_ce, #id, sizeof(#id) - 1, id); 516 | #include "constants.h" 517 | #undef AST_CONST 518 | 519 | old_compile_string = zend_compile_string; 520 | zend_compile_string = astkit_compile_string; 521 | 522 | old_ast_process = zend_ast_process; 523 | zend_ast_process = astkit_ast_process; 524 | 525 | return SUCCESS; 526 | } 527 | 528 | int astkit_node_mshutdown(SHUTDOWN_FUNC_ARGS) { 529 | zend_compile_string = old_compile_string; 530 | zend_ast_process = old_ast_process; 531 | return SUCCESS; 532 | } 533 | -------------------------------------------------------------------------------- /astkit-zval.c: -------------------------------------------------------------------------------- 1 | #include "astkit.h" 2 | 3 | zend_class_entry* astkit_zval_ce = NULL; 4 | 5 | /* {{{ proto int AstKitZval::numChildren() */ 6 | static PHP_METHOD(AstKitZval, numChildren) { 7 | RETURN_LONG(0); 8 | } /* }}} */ 9 | 10 | /* {{{ proto object AstKitZval::getChild(int child[, bool zval_as_value = true]) */ 11 | ZEND_BEGIN_ARG_INFO(AstKitZval_getChild_arginfo, 0) 12 | ZEND_ARG_INFO(0, child) 13 | ZEND_ARG_INFO(0, zval_as_value) 14 | ZEND_END_ARG_INFO() 15 | static PHP_METHOD(AstKitZval, getChild) { 16 | RETURN_NULL(); 17 | } /* }}} */ 18 | 19 | /* {{{ proto object AstKitZval::getValue() */ 20 | static PHP_METHOD(AstKitZval, getValue) { 21 | astkit_object* objval = ASTKIT_FETCH_OBJ(getThis()); 22 | zval* val = zend_ast_get_zval(objval->node); 23 | RETURN_ZVAL(val, 1, 0); 24 | } /* }}} */ 25 | 26 | static zend_function_entry astkit_zval_methods[] = { 27 | PHP_ME(AstKitZval, numChildren, NULL, ZEND_ACC_PUBLIC) 28 | PHP_ME(AstKitZval, getChild, AstKitZval_getChild_arginfo, ZEND_ACC_PUBLIC) 29 | PHP_ME(AstKitZval, getValue, NULL, ZEND_ACC_PUBLIC) 30 | PHP_FE_END 31 | }; 32 | 33 | int astkit_zval_minit(INIT_FUNC_ARGS) { 34 | zend_class_entry ce; 35 | 36 | INIT_CLASS_ENTRY(ce, "AstKitZval", astkit_zval_methods); 37 | astkit_zval_ce = zend_register_internal_class_ex(&ce, astkit_node_ce); 38 | 39 | return SUCCESS; 40 | } 41 | -------------------------------------------------------------------------------- /astkit.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Sara Golemon | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #include "astkit.h" 22 | 23 | #include "ext/standard/info.h" 24 | 25 | #if PHP_MAJOR_VERSION < 7 26 | # error AstKit requires PHP version 7 or later 27 | #endif 28 | 29 | void astkit_create_object(zval* return_value, zend_ast* node, astkit_tree* tree, zend_bool zval_as_value) { 30 | zval *cached_object; 31 | zend_class_entry *ce = NULL; 32 | 33 | if (!node) { 34 | RETURN_NULL(); 35 | } 36 | 37 | cached_object = zend_hash_index_find(&ASTKITG(cache), (zend_ulong)node); 38 | if (cached_object) { 39 | RETURN_ZVAL(cached_object, 1, 0); 40 | } 41 | 42 | switch (node->kind) { 43 | case ZEND_AST_ZVAL: 44 | if (zval_as_value) { 45 | zval* val = zend_ast_get_zval(node); 46 | RETURN_ZVAL(val, 1, 0); 47 | return; 48 | } 49 | ce = astkit_zval_ce; 50 | break; 51 | #define AST(id) 52 | #define AST_DECL(id) case ZEND_AST_##id: ce = astkit_decl_ce; break; 53 | #define AST_LIST(id) case ZEND_AST_##id: ce = astkit_list_ce; break; 54 | #define AST_CHILD(id, children) case ZEND_AST_##id: ce = astkit_node_ce; break; 55 | #include "ast-names.h" 56 | #undef AST_CHILD 57 | #undef AST_LIST 58 | #undef AST_DECL 59 | #undef AST 60 | default: 61 | php_error_docref(NULL, E_WARNING, "Unknown AST node type: %d", (int)node->kind); 62 | RETURN_FALSE; 63 | } 64 | 65 | astkit_object* objval; 66 | object_init_ex(return_value, ce); 67 | objval = ASTKIT_FETCH_OBJ(return_value); 68 | objval->node = node; 69 | objval->tree = tree; 70 | ++tree->refcount; 71 | 72 | zend_hash_index_add(&ASTKITG(cache), (zend_ulong)node, return_value); 73 | } 74 | 75 | /* {{{ PHP_MINI_FUNCTION */ 76 | PHP_MINIT_FUNCTION(astkit) { 77 | return ((astkit_node_minit(INIT_FUNC_ARGS_PASSTHRU) == SUCCESS) && 78 | (astkit_list_minit(INIT_FUNC_ARGS_PASSTHRU) == SUCCESS) && 79 | (astkit_decl_minit(INIT_FUNC_ARGS_PASSTHRU) == SUCCESS) && 80 | (astkit_zval_minit(INIT_FUNC_ARGS_PASSTHRU) == SUCCESS)) 81 | ? SUCCESS : FAILURE; 82 | } /* }}} */ 83 | 84 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 85 | PHP_MSHUTDOWN_FUNCTION(astkit) { 86 | return ((astkit_node_mshutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU) == SUCCESS)) 87 | ? SUCCESS : FAILURE; 88 | } /* }}} */ 89 | 90 | /* {{{ PHP_MINFO_FUNCTION */ 91 | PHP_MINFO_FUNCTION(astkit) { 92 | php_info_print_table_start(); 93 | php_info_print_table_row(2, "astkit support", "enabled"); 94 | php_info_print_table_end(); 95 | } /* }}} */ 96 | 97 | ZEND_DECLARE_MODULE_GLOBALS(astkit); 98 | 99 | /* {{{ PHP_GINIT_FUNCTION */ 100 | static PHP_GINIT_FUNCTION(astkit) { 101 | #if defined(COMPILE_DL_ASTKIT) && defined(ZTS) 102 | ZEND_TSRMLS_CACHE_UPDATE(); 103 | #endif 104 | astkit_globals->hijack_ast = NULL; 105 | astkit_globals->hijack_ast_arena = NULL; 106 | zend_hash_init(&(astkit_globals->cache), 32, NULL, NULL, 1); 107 | zend_hash_init(&(astkit_globals->orphan), 8, NULL, NULL, 1); 108 | } 109 | /* }}} */ 110 | 111 | /* {{{ astkit_module_entry 112 | */ 113 | zend_module_entry astkit_module_entry = { 114 | STANDARD_MODULE_HEADER, 115 | "astkit", 116 | NULL, /* functions */ 117 | PHP_MINIT(astkit), 118 | PHP_MSHUTDOWN(astkit), 119 | NULL, /* RINIT */ 120 | NULL, /* RSHUTDOWN */ 121 | PHP_MINFO(astkit), 122 | "7.0.0-dev", 123 | PHP_MODULE_GLOBALS(astkit), 124 | PHP_GINIT(astkit), 125 | NULL, /* GSHUTDOWN */ 126 | NULL, /* RPOSTSHUTDOWN */ 127 | STANDARD_MODULE_PROPERTIES_EX 128 | }; 129 | /* }}} */ 130 | 131 | #ifdef COMPILE_DL_ASTKIT 132 | ZEND_GET_MODULE(astkit) 133 | #endif 134 | 135 | /* 136 | * Local variables: 137 | * tab-width: 4 138 | * c-basic-offset: 4 139 | * End: 140 | * vim600: noet sw=4 ts=4 fdm=marker 141 | * vim<600: noet sw=4 ts=4 142 | */ 143 | -------------------------------------------------------------------------------- /astkit.h: -------------------------------------------------------------------------------- 1 | #ifndef incl_PHP_ASTKIT_H 2 | #define incl_PHP_ASTKIT_H 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include "config.h" 6 | #endif 7 | 8 | #include "php.h" 9 | #include "zend_ast.h" 10 | #include "zend_arena.h" 11 | 12 | typedef struct _astkit_tree { 13 | zend_ast* root; 14 | zend_arena* arena; 15 | int refcount; 16 | } astkit_tree; 17 | 18 | typedef struct _astkit_object { 19 | zend_ast* node; 20 | astkit_tree* tree; 21 | zend_object std; 22 | } astkit_object; 23 | 24 | ZEND_BEGIN_MODULE_GLOBALS(astkit) 25 | /* When non-null, the next invocation of eval() will: 26 | * * Bypass any opcache in use 27 | * * Destroy the dummy ast/ast_arena during ast_process 28 | * * Replace them with these 29 | * 30 | * It was that, or reimplement the compiler... 31 | */ 32 | zend_ast* hijack_ast; 33 | zend_arena* hijack_ast_arena; 34 | 35 | /* Cache of zend_ast* nodes to object instances */ 36 | HashTable cache; 37 | 38 | /* Nodes which are no longer attached to their root */ 39 | HashTable orphan; 40 | ZEND_END_MODULE_GLOBALS(astkit) 41 | 42 | #if defined(ZTS) && defined(COMPILE_DL_ASTKIT) 43 | ZEND_TSRMLS_CACHE_EXTERN(); 44 | #endif 45 | 46 | ZEND_EXTERN_MODULE_GLOBALS(astkit) 47 | #define ASTKITG(v) ZEND_MODULE_GLOBALS_ACCESSOR(astkit, v) 48 | 49 | void astkit_create_object(zval*, zend_ast*, astkit_tree*, zend_bool); 50 | 51 | extern zend_class_entry* astkit_node_ce; 52 | int astkit_node_minit(INIT_FUNC_ARGS); 53 | int astkit_node_mshutdown(SHUTDOWN_FUNC_ARGS); 54 | zend_ast* astkit_ast_copy(zend_ast* ast); 55 | void astkit_graft(astkit_object* objval, int child, zval* value); 56 | 57 | extern zend_class_entry* astkit_list_ce; 58 | int astkit_list_minit(INIT_FUNC_ARGS); 59 | 60 | extern zend_class_entry* astkit_decl_ce; 61 | int astkit_decl_minit(INIT_FUNC_ARGS); 62 | 63 | extern zend_class_entry* astkit_zval_ce; 64 | int astkit_zval_minit(INIT_FUNC_ARGS); 65 | 66 | static inline astkit_object* astkit_from_zend_object(zend_object *obj) { 67 | return ((astkit_object*)(obj + 1)) - 1; 68 | } 69 | 70 | static inline zend_object* astkit_to_zend_object(astkit_object* obj) { 71 | return &(obj->std); 72 | } 73 | 74 | static inline astkit_object* ASTKIT_FETCH_OBJ(zval *obj) { 75 | ZEND_ASSERT(Z_TYPE_P(obj) == IS_OBJECT); 76 | ZEND_ASSERT(instanceof_function(Z_OBJCE_P(obj), astkit_node_ce)); 77 | return astkit_from_zend_object(Z_OBJ_P(obj)); 78 | } 79 | 80 | #ifndef ZEND_ACC_CTOR 81 | #define ZEND_ACC_CTOR 0 82 | #endif 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension astkit 3 | 4 | PHP_ARG_ENABLE(astkit, whether to enable astkit support, 5 | [ --disable-astkit Disable astkit support], yes) 6 | 7 | if test "$PHP_ASTKIT" != "no"; then 8 | PHP_NEW_EXTENSION(astkit, astkit.c astkit-node.c astkit-list.c astkit-decl.c astkit-zval.c, $ext_shared) 9 | fi 10 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | ARG_ENABLE("astkit", "whether to enable astkit support", "no"); 5 | 6 | if (PHP_ASTKIT != "no") { 7 | EXTENSION("astkit", "astkit.c astkit-node.c astkit-list.c astkit-decl.c"); 8 | } 9 | -------------------------------------------------------------------------------- /constants.h: -------------------------------------------------------------------------------- 1 | AST_CONST(IS_UNDEF); 2 | AST_CONST(IS_NULL); 3 | AST_CONST(IS_FALSE); 4 | AST_CONST(IS_TRUE); 5 | AST_CONST(IS_LONG); 6 | AST_CONST(IS_DOUBLE); 7 | AST_CONST(IS_STRING); 8 | AST_CONST(IS_ARRAY); 9 | AST_CONST(IS_OBJECT); 10 | AST_CONST(IS_RESOURCE); 11 | AST_CONST(IS_REFERENCE); 12 | 13 | #ifdef IS_CONSTANT 14 | AST_CONST(IS_CONSTANT); 15 | #endif 16 | AST_CONST(IS_CONSTANT_AST); 17 | AST_CONST(_IS_BOOL); 18 | AST_CONST(IS_CALLABLE); 19 | #if PHP_VERSION_ID >= 70100 20 | AST_CONST(IS_VOID); 21 | AST_CONST(_IS_ERROR); 22 | #endif 23 | AST_CONST(IS_INDIRECT); 24 | AST_CONST(IS_PTR); 25 | 26 | AST_CONST(ZEND_ACC_STATIC); 27 | AST_CONST(ZEND_ACC_ABSTRACT); 28 | AST_CONST(ZEND_ACC_FINAL); 29 | #ifdef ZEND_ACC_IMPLEMENTED_ABSTRACT 30 | AST_CONST(ZEND_ACC_IMPLEMENTED_ABSTRACT); 31 | #endif 32 | #ifdef ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 33 | AST_CONST(ZEND_ACC_IMPLICIT_ABSTRACT_CLASS); 34 | #endif 35 | #ifdef ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 36 | AST_CONST(ZEND_ACC_EXPLICIT_ABSTRACT_CLASS); 37 | #endif 38 | AST_CONST(ZEND_ACC_INTERFACE); 39 | AST_CONST(ZEND_ACC_TRAIT); 40 | AST_CONST(ZEND_ACC_ANON_CLASS); 41 | #ifdef ZEND_ACC_ANON_BOUND 42 | AST_CONST(ZEND_ACC_ANON_BOUND); 43 | #endif 44 | AST_CONST(ZEND_ACC_PUBLIC); 45 | AST_CONST(ZEND_ACC_PROTECTED); 46 | AST_CONST(ZEND_ACC_PRIVATE); 47 | AST_CONST(ZEND_ACC_PPP_MASK); 48 | AST_CONST(ZEND_ACC_CHANGED); 49 | #ifdef ZEND_ACC_IMPLICIT_PUBLIC 50 | AST_CONST(ZEND_ACC_IMPLICIT_PUBLIC); 51 | #endif 52 | #ifdef ZEND_ACC_CTOR 53 | AST_CONST(ZEND_ACC_CTOR); 54 | #endif 55 | #ifdef ZEND_ACC_DTOR 56 | AST_CONST(ZEND_ACC_DTOR); 57 | #endif 58 | #ifdef ZEND_ACC_CLOSE 59 | AST_CONST(ZEND_ACC_CLONE); 60 | #endif 61 | AST_CONST(ZEND_ACC_USER_ARG_INFO); 62 | AST_CONST(ZEND_ACC_ALLOW_STATIC); 63 | #ifdef ZEND_ACC_SHADOW 64 | AST_CONST(ZEND_ACC_SHADOW); 65 | #endif 66 | AST_CONST(ZEND_ACC_DEPRECATED); 67 | AST_CONST(ZEND_ACC_IMPLEMENT_INTERFACES); 68 | AST_CONST(ZEND_ACC_IMPLEMENT_TRAITS); 69 | AST_CONST(ZEND_ACC_CONSTANTS_UPDATED); 70 | AST_CONST(ZEND_HAS_STATIC_IN_METHODS); 71 | AST_CONST(ZEND_ACC_CLOSURE); 72 | AST_CONST(ZEND_ACC_GENERATOR); 73 | #ifdef ZEND_ACC_NO_RT_ARENA 74 | AST_CONST(ZEND_ACC_NO_RT_ARENA); 75 | #endif 76 | AST_CONST(ZEND_ACC_CALL_VIA_TRAMPOLINE); 77 | AST_CONST(ZEND_ACC_CALL_VIA_HANDLER); 78 | AST_CONST(ZEND_ACC_NEVER_CACHE); 79 | AST_CONST(ZEND_ACC_VARIADIC); 80 | AST_CONST(ZEND_ACC_RETURN_REFERENCE); 81 | AST_CONST(ZEND_ACC_DONE_PASS_TWO); 82 | AST_CONST(ZEND_ACC_USE_GUARDS); 83 | AST_CONST(ZEND_ACC_HAS_TYPE_HINTS); 84 | AST_CONST(ZEND_ACC_HAS_FINALLY_BLOCK); 85 | AST_CONST(ZEND_ACC_ARENA_ALLOCATED); 86 | AST_CONST(ZEND_ACC_HAS_RETURN_TYPE); 87 | AST_CONST(ZEND_ACC_STRICT_TYPES); 88 | 89 | AST_CONST(ZEND_NAME_FQ); 90 | AST_CONST(ZEND_NAME_RELATIVE); 91 | AST_CONST(ZEND_NAME_NOT_FQ); 92 | 93 | #if PHP_VERSION_ID >= 70100 94 | AST_CONST(ZEND_ARRAY_SYNTAX_LIST); 95 | AST_CONST(ZEND_ARRAY_SYNTAX_SHORT); 96 | AST_CONST(ZEND_ARRAY_SYNTAX_LONG); 97 | #endif 98 | 99 | AST_CONST(ZEND_PARAM_REF); 100 | AST_CONST(ZEND_PARAM_VARIADIC); 101 | 102 | AST_CONST(ZEND_INCLUDE); 103 | AST_CONST(ZEND_INCLUDE_ONCE); 104 | AST_CONST(ZEND_REQUIRE); 105 | AST_CONST(ZEND_REQUIRE_ONCE); 106 | AST_CONST(ZEND_EVAL); 107 | 108 | AST_CONST(ZEND_BW_NOT); 109 | AST_CONST(ZEND_BOOL_NOT); 110 | 111 | AST_CONST(ZEND_ASSIGN_ADD); 112 | AST_CONST(ZEND_ASSIGN_SUB); 113 | AST_CONST(ZEND_ASSIGN_MUL); 114 | AST_CONST(ZEND_ASSIGN_DIV); 115 | AST_CONST(ZEND_ASSIGN_MOD); 116 | AST_CONST(ZEND_ASSIGN_SL); 117 | AST_CONST(ZEND_ASSIGN_SR); 118 | AST_CONST(ZEND_ASSIGN_CONCAT); 119 | AST_CONST(ZEND_ASSIGN_BW_OR); 120 | AST_CONST(ZEND_ASSIGN_BW_AND); 121 | AST_CONST(ZEND_ASSIGN_BW_XOR); 122 | AST_CONST(ZEND_ASSIGN_POW); 123 | 124 | AST_CONST(ZEND_ADD); 125 | AST_CONST(ZEND_SUB); 126 | AST_CONST(ZEND_MUL); 127 | AST_CONST(ZEND_DIV); 128 | AST_CONST(ZEND_MOD); 129 | AST_CONST(ZEND_SL); 130 | AST_CONST(ZEND_SR); 131 | AST_CONST(ZEND_CONCAT); 132 | AST_CONST(ZEND_BW_OR); 133 | AST_CONST(ZEND_BW_AND); 134 | AST_CONST(ZEND_BW_XOR); 135 | AST_CONST(ZEND_IS_IDENTICAL); 136 | AST_CONST(ZEND_IS_NOT_IDENTICAL); 137 | AST_CONST(ZEND_IS_EQUAL); 138 | AST_CONST(ZEND_IS_NOT_EQUAL); 139 | AST_CONST(ZEND_IS_SMALLER); 140 | AST_CONST(ZEND_IS_SMALLER_OR_EQUAL); 141 | AST_CONST(ZEND_POW); 142 | AST_CONST(ZEND_BOOL_XOR); 143 | AST_CONST(ZEND_SPACESHIP); 144 | 145 | -------------------------------------------------------------------------------- /php_astkit.h: -------------------------------------------------------------------------------- 1 | #ifndef incl_PHP_ASTKIT_H 2 | #define incl_PHP_ASTKIT_H 3 | 4 | #include "php.h" 5 | 6 | extern zend_module_entry astkit_module_entry; 7 | #define phpext_astkit_ptr &astkit_module_entry 8 | 9 | #endif /* incl_PHP_ASTKIT_H */ 10 | -------------------------------------------------------------------------------- /tests/bad-graft-00.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Graft statement into non statement list 00 3 | --FILE-- 4 | graft(0, "foo"); 10 | 11 | astkit_dump($ast); 12 | 13 | $ast->execute(); 14 | echo "Done\n"; 15 | --EXPECT-- 16 | ZEND_AST_STMT_LIST 17 | ZEND_AST_ZVAL string(3) "foo" 18 | Done 19 | -------------------------------------------------------------------------------- /tests/cache.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Make sure astkit nodes are cached, not recreated 3 | --FILE-- 4 | getChild(0); 12 | $echo2 = $ast->getChild(0); 13 | var_dump($echo1 === $echo2); 14 | --EXPECT-- 15 | bool(true) 16 | -------------------------------------------------------------------------------- /tests/clone.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Cloned AST trees 3 | --FILE-- 4 | getChild(0)->getChild(0); 16 | $foo = clone $ast->getChild(1); 17 | 18 | astkit_dump($ast); 19 | unset($ast); 20 | astkit_dump($echo); 21 | astkit_dump($foo); 22 | --EXPECT-- 23 | ZEND_AST_STMT_LIST 24 | ZEND_AST_STMT_LIST 25 | ZEND_AST_ECHO 26 | ZEND_AST_ZVAL string(12) "Hello+World%0A" 27 | ZEND_AST_FUNC_DECL 28 | ZEND_AST_PARAM_LIST 29 | ZEND_AST_STMT_LIST 30 | ZEND_AST_STMT_LIST 31 | ZEND_AST_ECHO 32 | ZEND_AST_ZVAL string(8) "Goodbye%0A" 33 | ZEND_AST_ECHO 34 | ZEND_AST_ZVAL string(12) "Hello+World%0A" 35 | ZEND_AST_FUNC_DECL 36 | ZEND_AST_PARAM_LIST 37 | ZEND_AST_STMT_LIST 38 | ZEND_AST_STMT_LIST 39 | ZEND_AST_ECHO 40 | ZEND_AST_ZVAL string(8) "Goodbye%0A" 41 | -------------------------------------------------------------------------------- /tests/execute.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Execute a fragment of an AST 3 | --FILE-- 4 | getChild(0)->execute(); 18 | var_dump(function_exists('foo')); 19 | $ast->getChild(1)->execute(); 20 | var_dump(function_exists('foo')); 21 | var_dump(foo()); 22 | 23 | --EXPECT-- 24 | ZEND_AST_STMT_LIST 25 | ZEND_AST_STMT_LIST 26 | ZEND_AST_ECHO 27 | ZEND_AST_ZVAL string(12) "Hello+World%0A" 28 | ZEND_AST_FUNC_DECL 29 | ZEND_AST_PARAM_LIST 30 | ZEND_AST_STMT_LIST 31 | ZEND_AST_RETURN 32 | ZEND_AST_ZVAL string(3) "bar" 33 | bool(false) 34 | Hello World 35 | bool(false) 36 | bool(true) 37 | string(3) "bar" 38 | -------------------------------------------------------------------------------- /tests/export/fragment.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Export a portion of a parsed AST 3 | --FILE-- 4 | numChildren(); 21 | for ($i = 0; $i < $children; ++$i) { 22 | $node = $ast->getChild($i); 23 | if (($node instanceof AstKitDecl) && 24 | ($node->getId() == AstKit::ZEND_AST_FUNC_DECL)) { 25 | echo "Definition of ", $node->getName(), "("; 26 | $params = $node->getParams(); 27 | if ($params) { echo $params->export(); } 28 | echo "):\n"; 29 | echo "----\n"; 30 | $stmts = $node->getStatements(); 31 | if ($stmts) { echo $stmts->export(), "\n"; } 32 | } 33 | } 34 | 35 | --EXPECT-- 36 | Definition of foo($bar, $baz = 123): 37 | ---- 38 | echo $bar + $baz; 39 | 40 | Definition of qux(array $bling): 41 | ---- 42 | var_dump($bling); 43 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_arg_list.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_ARG_LIST 3 | --FILE-- 4 | export(); 23 | 24 | --EXPECT-- 25 | function foo($a, &$b, array $c, array $d = null, bool $e = true, int $f = 42, float $g = 3.14, string $h = 'hello', stdClass $i = null) { 26 | return $e; 27 | } -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_ARG_LIST 3 | --FILE-- 4 | null, 22 | 'true' => true, 23 | 'false' => false, 24 | 'int' => 42, 25 | 'float' => 3.14, 26 | /* next index */ M_PI, 27 | 'string' => "Hello World", 28 | 'array' => [1,2,3], 29 | 'expr' => $x . $y, 30 | ]; 31 | 32 | EOD 33 | )->export(); 34 | 35 | --EXPECT-- 36 | []; 37 | [null]; 38 | [true]; 39 | [false]; 40 | [42]; 41 | [3.14]; 42 | [M_PI]; 43 | ['Hello World']; 44 | [[1, 2, 3]]; 45 | [$x . $y]; 46 | [null, true, false, 42, 3.14, M_PI, 'Hello World', [1, 2, 3], $x . $y]; 47 | ['null' => null, 'true' => true, 'false' => false, 'int' => 42, 'float' => 3.14, M_PI, 'string' => 'Hello World', 'array' => [1, 2, 3], 'expr' => $x . $y]; 48 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_class.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_CLASS 3 | --FILE-- 4 | export(); 12 | 13 | --EXPECT-- 14 | class Foo extends Bar implements Baz, Qux { 15 | } 16 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_closure.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_CLOSURE 3 | --FILE-- 4 | export(); 15 | 16 | --EXPECT-- 17 | function ($a, &$b, $c = 'd', stdClass $e = null) { 18 | $b = $a; 19 | return $c . (string)$e; 20 | }; -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_encaps_list.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_ENCAPS_LIST 3 | --FILE-- 4 | export(); 18 | 19 | --EXPECT-- 20 | "foo $bar baz"; 21 | "foo $bar baz"; 22 | "foo $bar baz"; 23 | "foo {${bar}} baz"; 24 | "foo {${bar}} baz"; 25 | "foo {${bar}} baz"; 26 | "foo {${bar}} baz"; 27 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_func_decl.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_FUNC_DECL 3 | --FILE-- 4 | export(); 15 | 16 | --EXPECT-- 17 | function foo($bar, &$refbar, $baz = 'blong', array $qux = null) { 18 | $refbar = $bar; 19 | return $baz . ', ' . implode(', ', $qux); 20 | } 21 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_if.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_IF 3 | --FILE-- 4 | $y) { 13 | return 3; 14 | } else { 15 | return 4; 16 | } 17 | 18 | EOD 19 | )->export(); 20 | 21 | --EXPECT-- 22 | if (true) { 23 | return 1; 24 | } else if ($x) { 25 | return 2; 26 | } elseif ($x > $y) { 27 | return 3; 28 | } else { 29 | return 4; 30 | } -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_method.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_METHOD 3 | --FILE-- 4 | export(); 20 | 21 | --EXPECT-- 22 | class Foo { 23 | public function bar($a, &$b, array $c, SPLDoohickie $d = null) { 24 | return 42; 25 | } 26 | 27 | protected static final function bar() { 28 | return true; 29 | } 30 | 31 | private function qux() { 32 | return false; 33 | } 34 | 35 | public function elephpant($x) { 36 | return $x; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_switch.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_SWITCH 3 | --FILE-- 4 | export(); 23 | 24 | --EXPECT-- 25 | switch (foo()) { 26 | case 1: 27 | echo 'Hello'; 28 | break; 29 | case 'two': 30 | echo 'Goodbye'; 31 | break; 32 | case bar(): 33 | exit(1); 34 | default: 35 | exit(2); 36 | } -------------------------------------------------------------------------------- /tests/export/lang/zend_ast_zval.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | AST Export ZEND_AST_ZVAL 3 | --FILE-- 4 | export(); 18 | 19 | --EXPECT-- 20 | null; 21 | true; 22 | false; 23 | 123; 24 | 234.5; 25 | 'foo'; 26 | [1, 2, 3]; 27 | [$x]; 28 | -------------------------------------------------------------------------------- /tests/graft-echo.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Graft new value onto an ECHO node 3 | --FILE-- 4 | getChild(0)->getChild(0)->graft(0, "Goodbyte"); 14 | astkit_dump($ast); 15 | 16 | --EXPECT-- 17 | ZEND_AST_STMT_LIST 18 | ZEND_AST_STMT_LIST 19 | ZEND_AST_ECHO 20 | ZEND_AST_ZVAL string(12) "Hello+World%0A" 21 | ZEND_AST_STMT_LIST 22 | ZEND_AST_STMT_LIST 23 | ZEND_AST_ECHO 24 | ZEND_AST_ZVAL string(8) "Goodbyte" 25 | -------------------------------------------------------------------------------- /tests/graft-if-obj.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Graft new value onto a statement list replacing a non zval node 3 | --FILE-- 4 | execute(); 16 | $const = $if->getChild(0)->getChild(0); 17 | $const->graft(0, false); 18 | $if->execute(); 19 | --EXPECT-- 20 | This is a triumph. 21 | The cake is a lie. 22 | -------------------------------------------------------------------------------- /tests/graft-if.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Graft new value onto a statement list 3 | --FILE-- 4 | execute(); 16 | $const = $if->getChild(0)->getChild(0)->getChild(0); 17 | $const->graft(0, "false"); 18 | $if->execute(); 19 | --EXPECT-- 20 | This is a triumph. 21 | The cake is a lie. 22 | -------------------------------------------------------------------------------- /tests/graft-stmt.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Graft new value onto a statement list 3 | --FILE-- 4 | getChild(0)->getChild(0); 14 | 15 | $ast = new AstKitList; 16 | $ast->graft(0, $echo); 17 | $ast->graft(1, $echo); 18 | $ast->append($echo); 19 | 20 | astkit_dump($ast); 21 | $ast->execute(); 22 | 23 | --EXPECT-- 24 | ZEND_AST_STMT_LIST 25 | ZEND_AST_ECHO 26 | ZEND_AST_ZVAL string(10) "Hi+there%21%0A" 27 | ZEND_AST_ECHO 28 | ZEND_AST_ZVAL string(10) "Hi+there%21%0A" 29 | ZEND_AST_ECHO 30 | ZEND_AST_ZVAL string(10) "Hi+there%21%0A" 31 | Hi there! 32 | Hi there! 33 | Hi there! 34 | -------------------------------------------------------------------------------- /tests/new.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Create new AST tree with an empty statement list 3 | --FILE-- 4 | getChild(0)->getChild(0); 13 | $call = $echo->getChild(0); 14 | 15 | $echo->graft(0, "no\n"); 16 | 17 | astkit_dump($ast); 18 | --EXPECT-- 19 | ZEND_AST_STMT_LIST 20 | ZEND_AST_STMT_LIST 21 | ZEND_AST_ECHO 22 | ZEND_AST_ZVAL string(3) "no%0A" 23 | 24 | -------------------------------------------------------------------------------- /tests/simple-psuedomain.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Basic statements in a psuedomain 3 | --FILE-- 4 | getId()); 6 | if ($attrs = $ast->getAttributes()) { 7 | printf("-attrs(%04x)", $attrs); 8 | } 9 | if ($ast instanceof AstKindDecl) { 10 | if ($ast->getId() != AstKind::ZEND_AST_CLOSURE) { 11 | echo ' ', $ast->getName(); 12 | if ($ast->getId() != AstKind::ZEND_AST_CLASS) { 13 | echo '()'; 14 | } 15 | } 16 | if ($flags = $ast->getFlags()) { 17 | printf(" flags(%04x)", $flags); 18 | } 19 | } 20 | echo "\n"; 21 | 22 | $vardump = function($val, $indent = 0) { 23 | echo str_repeat(' ', 2 * $indent); 24 | echo "ZEND_AST_ZVAL "; 25 | if (is_null($val) || is_bool($val) || 26 | is_int($val) || is_float($val)) { 27 | var_dump($val); 28 | } elseif (is_string($val)) { 29 | echo 'string(', strlen($val), ') "', urlencode($val), "\"\n"; 30 | } else { 31 | // shouldn't happen 32 | echo gettype($val), "\n"; 33 | } 34 | }; 35 | 36 | $children = $ast->numChildren(); 37 | for ($i = 0; $i < $children; ++$i) { 38 | $child = $ast->getChild($i); 39 | if ($child === null) continue; 40 | if ($child instanceof AstKit) { 41 | astkit_dump($child, $indent + 1); 42 | } else { 43 | $vardump($child, $indent + 1); 44 | } 45 | } 46 | } 47 | --------------------------------------------------------------------------------