From 590689b816afc16902733a9db9ddcc6b5cb92d43 Mon Sep 17 00:00:00 2001 From: codinghuang <2812240764@qq.com> Date: Sun, 13 Dec 2020 20:00:33 +0800 Subject: [PATCH] Support phpstorm (#22) * Support phpstorm --- .gitignore | 2 - header.php | 3 + include/common.h | 4 +- include/remote_debugger.h | 17 +- include/util.h | 11 +- include/zend_property_info.h | 28 +++ php_yasd.h | 1 + src/common.cc | 46 +++++ src/remote_debugger.cc | 349 ++++++++++++++++++++++++++++------- src/util.cc | 188 ++++++++++++++++++- test.php | 109 +++++++++++ yasd.cc | 2 + 12 files changed, 685 insertions(+), 75 deletions(-) create mode 100644 header.php create mode 100644 include/zend_property_info.h create mode 100644 test.php diff --git a/.gitignore b/.gitignore index 3ece45f..2fafc81 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,6 @@ mkinstalldirs # debug debug -test.php -header.php composer.json composer.lock vendor diff --git a/header.php b/header.php new file mode 100644 index 0000000..5df0738 --- /dev/null +++ b/header.php @@ -0,0 +1,3 @@ + &options, std::string option); + + static zval *fetch_zval_by_fullname(std::string fullname); }; } // namespace yasd diff --git a/include/zend_property_info.h b/include/zend_property_info.h new file mode 100644 index 0000000..7319ae5 --- /dev/null +++ b/include/zend_property_info.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | Yasd | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: codinghuang | + +----------------------------------------------------------------------+ +*/ +#pragma once + +#include + +#include "main/php.h" + +namespace yasd { +class ZendPropertyInfo { + public: + zend_string *property_name; + zval *value; +}; +} // namespace yasd diff --git a/php_yasd.h b/php_yasd.h index c3abd3a..d025322 100644 --- a/php_yasd.h +++ b/php_yasd.h @@ -29,6 +29,7 @@ ZEND_BEGIN_MODULE_GLOBALS(yasd) char *debug_mode; char *remote_host; uint16_t remote_port; + uint16_t depth; ZEND_END_MODULE_GLOBALS(yasd) extern ZEND_DECLARE_MODULE_GLOBALS(yasd); diff --git a/src/common.cc b/src/common.cc index 4d86167..46ed468 100644 --- a/src/common.cc +++ b/src/common.cc @@ -21,8 +21,54 @@ char yasd_info_buf[YASD_MSG_SIZE]; zend_bool yasd_zend_hash_is_recursive(zend_array* ht) { return (ZEND_HASH_GET_APPLY_COUNT(ht) > 0); } + +zend_bool yasd_zend_hash_apply_protection_begin(zend_array* ht) { + if (!ht) { + return 1; + } + if (ZEND_HASH_GET_APPLY_COUNT(ht) > 0) { + return 0; + } + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ZEND_HASH_INC_APPLY_COUNT(ht); + } + return 1; +} + +zend_bool yasd_zend_hash_apply_protection_end(zend_array* ht) { + if (!ht) { + return 1; + } + if (ZEND_HASH_GET_APPLY_COUNT(ht) == 0) { + return 0; + } + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ZEND_HASH_DEC_APPLY_COUNT(ht); + } + return 1; +} #else /* PHP 7.3 or later */ zend_bool yasd_zend_hash_is_recursive(zend_array* ht) { return GC_IS_RECURSIVE(ht); } + +zend_bool yasd_zend_hash_apply_protection_begin(zend_array* ht) { +if (GC_IS_RECURSIVE(ht)) { + return 0; + } + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_PROTECT_RECURSION(ht); + } + return 1; +} + +zend_bool yasd_zend_hash_apply_protection_end(zend_array* ht) { + if (!GC_IS_RECURSIVE(ht)) { + return 0; + } + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(ht); + } + return 1; +} #endif diff --git a/src/remote_debugger.cc b/src/remote_debugger.cc index 29a21fd..6ac007a 100644 --- a/src/remote_debugger.cc +++ b/src/remote_debugger.cc @@ -22,6 +22,7 @@ #include "include/common.h" #include "include/base64.h" #include "include/remote_debugger.h" +#include "include/zend_property_info.h" #include "./php_yasd.h" @@ -63,22 +64,27 @@ void RemoteDebugger::init() { std::string RemoteDebugger::get_next_cmd() { ssize_t ret; + char c; char buffer[4096]; + char *p = buffer; + + // The IDE may send multiple commands, so we need to determine the delimiter. + do { + ret = recv(sock, &c, 1, 0); + if (ret == 0) { + // printf("connection closed\n"); + exit(255); + } + } while ((c != '\0') && (*p = c) && p++); - ret = recv(sock, buffer, 4096, 0); - if (ret == 0) { - // printf("connection closed\n"); - exit(255); - } // printf("recv: %ld\n", ret); - std::string tmp(buffer, buffer + ret); + std::string tmp(buffer, buffer + (p - buffer)); last_cmd = tmp; + printf("last_cmd: %s\n", last_cmd.c_str()); return last_cmd; } int RemoteDebugger::execute_cmd() { - // std::cout << last_cmd << std::endl; - auto exploded_cmd = yasd::Util::explode(last_cmd, " "); transaction_id = atoi(exploded_cmd[2].c_str()); @@ -177,7 +183,7 @@ ssize_t RemoteDebugger::send_doc(tinyxml2::XMLDocument *doc) { ssize_t ret; std::string message = make_message(doc); - // std::cout << message << std::endl; + std::cout << message << std::endl; ret = send(sock, message.c_str(), message.length(), 0); // printf("send: %ld\n", ret); @@ -193,12 +199,12 @@ std::string RemoteDebugger::make_message(tinyxml2::XMLDocument *doc) { doc->Print(&printer); - message = - message + std::to_string(printer.CStrSize() + sizeof("\n") - 1); + int size = printer.CStrSize() - 1 + sizeof("\n") - 1; + message = message + std::to_string(size); message += '\0'; message += "\n"; - message += '\0'; message += printer.CStr(); + message += '\0'; return message; } @@ -222,19 +228,28 @@ void RemoteDebugger::init_local_variables_xml_child_node(tinyxml2::XMLElement *r while (i < (unsigned int) op_array->last_var) { child = root->InsertNewChildElement("property"); zend_string *var_name = op_array->vars[i]; - child->SetAttribute("name", ZSTR_VAL(var_name)); - child->SetAttribute("fullname", ZSTR_VAL(var_name)); + std::string name = "$" + std::string(ZSTR_VAL(var_name)); + std::string fullname = "$" + std::string(ZSTR_VAL(var_name)); + child->SetAttribute("name", name.c_str()); + child->SetAttribute("fullname", fullname.c_str()); zval *var = yasd::Util::find_variable(ZSTR_VAL(var_name)); if (!var) { child->SetAttribute("type", "uninitialized"); } else { - set_property_value_xml_property_node(child, ZSTR_VAL(var_name), var); + init_xml_property_node(child, ZSTR_VAL(var_name), var); } i++; } + + if (Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT) { + child = root->InsertNewChildElement("property"); + child->SetAttribute("name", "$this"); + child->SetAttribute("fullname", "this"); + init_xml_property_node(child, "this", &EG(current_execute_data)->This); + } } void RemoteDebugger::init_superglobal_variables_xml_child_node(tinyxml2::XMLElement *root) { @@ -266,16 +281,18 @@ void RemoteDebugger::init_user_defined_constant_variables_xml_child_node(tinyxml child->SetAttribute("name", ZSTR_VAL(val->name)); child->SetAttribute("fullname", ZSTR_VAL(val->name)); child->SetAttribute("facet", "constant"); - set_property_value_xml_property_node(child, ZSTR_VAL(val->name), zval_value); + init_xml_property_node(child, ZSTR_VAL(val->name), zval_value); } ZEND_HASH_FOREACH_END(); return; } -void RemoteDebugger::set_property_value_xml_property_node(tinyxml2::XMLElement *child, - std::string name, - zval *value, - bool encoding) { +void RemoteDebugger::init_xml_property_node( + tinyxml2::XMLElement *child, std::string name, zval *value, int level, bool encoding) { + if (level > YASD_G(depth)) { + return; + } + switch (Z_TYPE_P(value)) { case IS_TRUE: child->SetAttribute("type", "bool"); @@ -299,6 +316,7 @@ void RemoteDebugger::set_property_value_xml_property_node(tinyxml2::XMLElement * case IS_STRING: child->SetAttribute("type", "string"); if (encoding) { + child->SetAttribute("size", (uint64_t) Z_STRLEN_P(value)); child->SetAttribute("encoding", "base64"); child->InsertNewText(base64_encode((unsigned char *) Z_STRVAL_P(value), Z_STRLEN_P(value)).c_str()) ->SetCData(true); @@ -306,54 +324,13 @@ void RemoteDebugger::set_property_value_xml_property_node(tinyxml2::XMLElement * child->InsertNewText(Z_STRVAL_P(value))->SetCData(true); } break; - case IS_ARRAY: { - zend_ulong num; - zend_string *key; - zval *val; - zend_array *ht = Z_ARRVAL_P(value); - - child->SetAttribute("type", "array"); - child->SetAttribute("children", ht->nNumOfElements > 0 ? "1" : "0"); - child->SetAttribute("numchildren", ht->nNumOfElements); - if (yasd_zend_hash_is_recursive(ht)) { - child->SetAttribute("recursive", 1); - } else { - ZEND_HASH_FOREACH_KEY_VAL_IND(ht, num, key, val) { - tinyxml2::XMLElement *property = child->InsertNewChildElement("property"); - std::string fullname; - std::string key_str; - - if (key == nullptr) { // num key - key_str = std::to_string(num); - property->SetAttribute("name", num); - fullname = "$" + name + "[" + std::to_string(num) + "]"; - } else { // string key - key_str = ZSTR_VAL(key); - property->SetAttribute("name", ZSTR_VAL(key)); - fullname = "$" + name + "[" + ZSTR_VAL(key) + "]"; - } - - property->SetAttribute("type", zend_zval_type_name(val)); - property->SetAttribute("fullname", fullname.c_str()); - set_property_value_xml_property_node(property, key_str, val, true); - // if (Z_TYPE_P(val) == IS_STRING) { - // property->SetAttribute("size", (uint64_t) Z_STRLEN_P(val)); - // property->SetAttribute("encoding", "base64"); - // property->InsertNewText(base64_encode((unsigned char *) Z_STRVAL_P(val), - // Z_STRLEN_P(val)).c_str()) - // ->SetCData(true); - // } else { - // property->InsertNewText(std::to_string(Z_LVAL_P(val)).c_str())->SetCData(true); - // } - } - ZEND_HASH_FOREACH_END(); - } + case IS_ARRAY: + init_zend_array_element_xml_property_node(child, name, value, level, encoding); break; - } - case IS_OBJECT: - child->SetAttribute("type", "object"); - child->InsertNewText("Object")->SetCData(true); + case IS_OBJECT: { + init_zend_object_property_xml_property_node(child, name, value, level, encoding); break; + } case IS_UNDEF: child->SetAttribute("type", "uninitialized"); break; @@ -362,6 +339,187 @@ void RemoteDebugger::set_property_value_xml_property_node(tinyxml2::XMLElement * } } +void RemoteDebugger::init_zend_array_element_xml_property_node( + tinyxml2::XMLElement *child, std::string name, zval *value, int level, bool encoding) { + zend_ulong num; + zend_string *key; + zval *val; + zend_array *ht = Z_ARRVAL_P(value); + + child->SetAttribute("type", "array"); + child->SetAttribute("children", ht->nNumOfElements > 0 ? "1" : "0"); + child->SetAttribute("numchildren", ht->nNumOfElements); + if (yasd_zend_hash_is_recursive(ht)) { + child->SetAttribute("recursive", 1); + } else { + ZEND_HASH_FOREACH_KEY_VAL_IND(ht, num, key, val) { + tinyxml2::XMLElement *property = child->InsertNewChildElement("property"); + std::string fullname; + std::string key_str; + + if (key == nullptr) { // num key + key_str = std::to_string(num); + property->SetAttribute("name", num); + fullname = name + "[" + std::to_string(num) + "]"; + } else { // string key + key_str = ZSTR_VAL(key); + property->SetAttribute("name", ZSTR_VAL(key)); + fullname = name + "[" + ZSTR_VAL(key) + "]"; + } + + property->SetAttribute("type", zend_zval_type_name(val)); + property->SetAttribute("fullname", fullname.c_str()); + level++; + init_xml_property_node(property, key_str, val, level, true); + if (level > YASD_G(depth)) { + child->DeleteChild(property); + } + level--; + } + ZEND_HASH_FOREACH_END(); + } +} + +void RemoteDebugger::init_zend_object_property_xml_property_node( + tinyxml2::XMLElement *child, std::string name, zval *value, int level, bool encoding) { + zend_string *class_name; + zend_array *properties; + class_name = Z_OBJCE_P(value)->name; + zend_ulong num; + zend_string *key; + zval *val; + properties = yasd::Util::get_properties(value); + + child->SetAttribute("type", "object"); + child->SetAttribute("classname", ZSTR_VAL(class_name)); + child->SetAttribute("children", properties->nNumOfElements > 0 ? "1" : "0"); + child->SetAttribute("numchildren", properties->nNumOfElements); + + std::vector summary_properties_info; + + // TODO(codinghuang): may we have a better way to get private properties + void *property_info; + ZEND_HASH_FOREACH_STR_KEY_PTR(&Z_OBJCE_P(value)->properties_info, key, property_info) { + ZendPropertyInfo info; + info.property_name = key; + summary_properties_info.emplace_back(info); + } + ZEND_HASH_FOREACH_END(); + + int i = 0; + ZEND_HASH_FOREACH_KEY_VAL_IND(properties, num, key, val) { + tinyxml2::XMLElement *property = child->InsertNewChildElement("property"); + std::string fullname; + std::string key_str; + + ZendPropertyInfo info = summary_properties_info[i]; + key = info.property_name; + + key_str = ZSTR_VAL(key); + property->SetAttribute("name", key_str.c_str()); + fullname = name + "->" + key_str; + + property->SetAttribute("fullname", fullname.c_str()); + property->SetAttribute("type", zend_zval_type_name(val)); + level++; + init_xml_property_node(property, key_str, val, level, true); + if (level > YASD_G(depth)) { + child->DeleteChild(property); + } + level--; + i++; + } + ZEND_HASH_FOREACH_END(); +} + +int RemoteDebugger::parse_feature_set_cmd() { + // https://xdebug.org/docs/dbgp#feature-set + + auto exploded_cmd = yasd::Util::explode(last_cmd, " "); + + std::unique_ptr doc(new tinyxml2::XMLDocument()); + + tinyxml2::XMLElement *root; + + root = doc->NewElement("response"); + doc->LinkEndChild(root); + init_response_xml_root_node(root, "feature_set"); + root->SetAttribute("feature", exploded_cmd[4].c_str()); + root->SetAttribute("success", 0); + + send_doc(doc.get()); + + return yasd::DebuggerModeBase::RECV_CMD_AGAIN; +} + +int RemoteDebugger::parse_stdout_cmd() { + // https://xdebug.org/docs/dbgp#stdout-stderr + + std::unique_ptr doc(new tinyxml2::XMLDocument()); + + tinyxml2::XMLElement *root; + + root = doc->NewElement("response"); + doc->LinkEndChild(root); + init_response_xml_root_node(root, "stdout"); + root->SetAttribute("success", 0); + + send_doc(doc.get()); + + return yasd::DebuggerModeBase::RECV_CMD_AGAIN; +} + +int RemoteDebugger::parse_status_cmd() { + // https://xdebug.org/docs/dbgp#status + + std::unique_ptr doc(new tinyxml2::XMLDocument()); + + tinyxml2::XMLElement *root; + + root = doc->NewElement("response"); + doc->LinkEndChild(root); + init_response_xml_root_node(root, "status"); + root->SetAttribute("status", "starting"); + root->SetAttribute("reason", "ok"); + + send_doc(doc.get()); + + return yasd::DebuggerModeBase::RECV_CMD_AGAIN; +} + +int RemoteDebugger::parse_eval_cmd() { + // https://xdebug.org/docs/dbgp#eval + + zval ret_zval; + auto exploded_cmd = yasd::Util::explode(last_cmd, " "); + + if (exploded_cmd[3] != "--") { + return yasd::DebuggerModeBase::FAILED; + } + + std::string eval_str = exploded_cmd[4]; + + eval_str = base64_decode(eval_str); + + yasd::Util::eval(const_cast(eval_str.c_str()), &ret_zval, const_cast("yasd://debug-eval")); + + std::unique_ptr doc(new tinyxml2::XMLDocument()); + + tinyxml2::XMLElement *root; + tinyxml2::XMLElement *child; + + root = doc->NewElement("response"); + doc->LinkEndChild(root); + init_response_xml_root_node(root, "eval"); + + child = root->InsertNewChildElement("property"); + init_xml_property_node(child, "", &ret_zval); + + send_doc(doc.get()); + + return yasd::DebuggerModeBase::RECV_CMD_AGAIN; +} + int RemoteDebugger::parse_breakpoint_list_cmd() { // https://xdebug.org/docs/dbgp#id7 @@ -562,6 +720,60 @@ int RemoteDebugger::parse_context_get_cmd() { return yasd::DebuggerModeBase::RECV_CMD_AGAIN; } +int RemoteDebugger::parse_property_get_cmd() { + // https://xdebug.org/docs/dbgp#property-get-property-set-property-value + + auto exploded_cmd = yasd::Util::explode(last_cmd, " "); + std::string name; + zval *zobj; + zval *property; + + if (exploded_cmd[0] != "property_get") { + return yasd::DebuggerModeBase::FAILED; + } + + std::unique_ptr doc(new tinyxml2::XMLDocument()); + tinyxml2::XMLElement *root; + tinyxml2::XMLElement *child; + + root = doc->NewElement("response"); + doc->LinkEndChild(root); + init_response_xml_root_node(root, "property_get"); + + name = yasd::Util::get_option_value(exploded_cmd, "-n"); + + // vscode has double quotes, but PhpStorm does not + if (name.front() == '"') { + name.erase(0, 1); + } + + if (name.back() == '"') { + name.pop_back(); + } + + // auto exploded_name = yasd::Util::explode(name, "->"); + + // if (Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT) { + // zobj = &EG(current_execute_data)->This; + // } else { + // zobj = yasd::Util::find_variable(exploded_name[0]); + // } + + property = yasd::Util::fetch_zval_by_fullname(name); + + // for (auto iter = exploded_name.begin() + 1; iter != exploded_name.end(); iter++) { + // property = yasd_zend_read_property(Z_OBJCE_P(zobj), zobj, iter->c_str(), iter->length(), 1); + // zobj = property; + // } + + child = root->InsertNewChildElement("property"); + init_xml_property_node(child, name, property); + + send_doc(doc.get()); + + return yasd::DebuggerModeBase::RECV_CMD_AGAIN; +} + int RemoteDebugger::parse_stop_cmd() { // there is no good way to shut down the server, // so let the debugger and the process separate first @@ -582,11 +794,16 @@ int RemoteDebugger::parse_stop_cmd() { } void RemoteDebugger::register_cmd_handler() { + handlers.emplace_back(std::make_pair("feature_set", std::bind(&RemoteDebugger::parse_feature_set_cmd, this))); + handlers.emplace_back(std::make_pair("stdout", std::bind(&RemoteDebugger::parse_stdout_cmd, this))); + handlers.emplace_back(std::make_pair("status", std::bind(&RemoteDebugger::parse_status_cmd, this))); + handlers.emplace_back(std::make_pair("eval", std::bind(&RemoteDebugger::parse_eval_cmd, this))); handlers.emplace_back( std::make_pair("breakpoint_list", std::bind(&RemoteDebugger::parse_breakpoint_list_cmd, this))); handlers.emplace_back(std::make_pair("breakpoint_set", std::bind(&RemoteDebugger::parse_breakpoint_set_cmd, this))); handlers.emplace_back(std::make_pair("run", std::bind(&RemoteDebugger::parse_run_cmd, this))); handlers.emplace_back(std::make_pair("stack_get", std::bind(&RemoteDebugger::parse_stack_get_cmd, this))); + handlers.emplace_back(std::make_pair("property_get", std::bind(&RemoteDebugger::parse_property_get_cmd, this))); handlers.emplace_back(std::make_pair("context_names", std::bind(&RemoteDebugger::parse_context_names_cmd, this))); handlers.emplace_back(std::make_pair("context_get", std::bind(&RemoteDebugger::parse_context_get_cmd, this))); handlers.emplace_back(std::make_pair("step_over", std::bind(&RemoteDebugger::parse_step_over_cmd, this))); diff --git a/src/util.cc b/src/util.cc index e0d7402..fb15c25 100644 --- a/src/util.cc +++ b/src/util.cc @@ -93,6 +93,32 @@ zval *Util::find_variable(std::string var_name) { return var; } +zval *Util::find_variable(zend_array *symbol_table, std::string var_name) { + zval *var; + + if (var_name == "this") { + return &EG(current_execute_data)->This; + } + + var = zend_hash_str_find(symbol_table, var_name.c_str(), var_name.length()); + + // not define variable + if (!var) { + return nullptr; + } + + while (Z_TYPE_P(var) == IS_INDIRECT) { + var = Z_INDIRECT_P(var); + } + + // the statement that defines the variable has not yet been executed + if (Z_TYPE_P(var) == IS_UNDEF) { + return nullptr; + } + + return var; +} + void Util::print_var(std::string var_name) { zval *var; zend_execute_data *execute_data = EG(current_execute_data); @@ -347,7 +373,7 @@ bool Util::is_hit_watch_point() { for (auto watchpointIter = var_watchpoint->second->begin(); watchpointIter != var_watchpoint->second->end(); watchpointIter++) { std::string var_name = watchpointIter->first; - yasd::WatchPointElement& watchpoint = watchpointIter->second; + yasd::WatchPointElement &watchpoint = watchpointIter->second; zval *new_var = yasd::Util::find_variable(var_name); if (new_var == nullptr) { @@ -396,4 +422,164 @@ bool Util::is_integer(const std::string &s) { return (*p == 0); } + +bool Util::eval(char *str, zval *retval_ptr, char *string_name) { + int origin_error_reporting = EG(error_reporting); + + // we need to turn off the warning if the variable is UNDEF + EG(error_reporting) = 0; + + int ret; + ret = zend_eval_string(str, retval_ptr, const_cast("xdebug://debug-eval")); + if (ret == FAILURE) { + return false; + } + + EG(error_reporting) = origin_error_reporting; + + return true; +} + +zend_array *Util::get_properties(zval *zobj) { +#if PHP_VERSION_ID >= 70400 + return zend_get_properties_for(zobj, ZEND_PROP_PURPOSE_VAR_EXPORT); +#else + if (Z_OBJ_HANDLER_P(zobj, get_properties)) { + return Z_OBJPROP_P(zobj); + } +#endif + return nullptr; +} + +std::string Util::get_option_value(const std::vector &options, std::string option) { + auto iter = options.begin(); + + for (; iter != options.end(); iter++) { + if (option == *iter) { + break; + } + } + + return *(++iter); +} + +// TODO(codinghuang): maybe we can use re2c and yacc later +zval *Util::fetch_zval_by_fullname(std::string fullname) { + std::string name; + int state = 0; + const char *ptr = fullname.c_str(); + const char *keyword = ptr, *keyword_end = nullptr; + const char *end = fullname.c_str() + fullname.length() - 1; + zval *retval_ptr = nullptr; + zval *zobj; + + zend_array *symbol_table = zend_rebuild_symbol_table(); + + // aa->bb->cc + // aa[bb][cc] + + do { + switch (state) { + case 0: + keyword = ptr; + state = 1; + case 1: + if (*ptr == '[') { + keyword_end = ptr; + if (keyword) { + std::string name(keyword, keyword_end - keyword); + + if (!retval_ptr) { + retval_ptr = find_variable(symbol_table, name); + } else if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { + retval_ptr = find_variable(Z_ARRVAL_P(retval_ptr), name); + } else { + retval_ptr = + yasd_zend_read_property(Z_OBJCE_P(retval_ptr), retval_ptr, name.c_str(), name.length(), 1); + } + keyword = nullptr; + } + state = 3; + } else if (*ptr == '-') { + keyword_end = ptr; + if (keyword) { + std::string name(keyword, keyword_end - keyword); + + if (!retval_ptr) { + retval_ptr = find_variable(symbol_table, name); + } else if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { + retval_ptr = find_variable(Z_ARRVAL_P(retval_ptr), name); + } else { + retval_ptr = + yasd_zend_read_property(Z_OBJCE_P(retval_ptr), retval_ptr, name.c_str(), name.length(), 1); + } + keyword = nullptr; + } + state = 2; + } + break; + case 2: + assert(*ptr == '>'); + keyword = ptr + 1; + state = 1; + break; + case 3: + if ((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z')) { + state = 6; + keyword = ptr; + } else if (*ptr >= '0' && *ptr <= '9') { + state = 6; + keyword = ptr; + } + break; + case 4: + break; + case 5: + if (*ptr == ']') { + state = 1; + } + break; + case 6: + if (*ptr == ']') { + keyword_end = ptr; + if (keyword) { + std::string name(keyword, keyword_end - keyword); + + if (!retval_ptr) { + retval_ptr = find_variable(symbol_table, name); + } else if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { + retval_ptr = find_variable(Z_ARRVAL_P(retval_ptr), name); + } else { + retval_ptr = + yasd_zend_read_property(Z_OBJCE_P(retval_ptr), retval_ptr, name.c_str(), name.length(), 1); + } + keyword = nullptr; + } + state = 1; + } + break; + default: + break; + } + ptr++; + } while (ptr <= end); + + if (keyword) { + keyword_end = ptr; + std::string name(keyword, keyword_end - keyword); + + if (!retval_ptr) { + retval_ptr = find_variable(symbol_table, name); + } else if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { + retval_ptr = find_variable(Z_ARRVAL_P(retval_ptr), name); + } else { + retval_ptr = + yasd_zend_read_property(Z_OBJCE_P(retval_ptr), retval_ptr, name.c_str(), name.length(), 1); + } + keyword = nullptr; + } + + return retval_ptr; +} + } // namespace yasd diff --git a/test.php b/test.php new file mode 100644 index 0000000..51b1d5a --- /dev/null +++ b/test.php @@ -0,0 +1,109 @@ +a = "hello"; + $this->c = ['a', 'b']; + } +} + +class Foo2 +{ + public $a; + + private $foo3; + + /** + * Map of entries that are already resolved. + * + * @var array + */ + private $resolvedEntries = []; + + public function __construct() + { + $this->a = "hello"; + $this->resolvedEntries = [ + 'aaa' => new Foo3(), + 'bbb' => [4, 5, 6], + ]; + $this->foo3 = new Foo3(); + } +} + +class Foo +{ + public $a; + + public $foo2; + + public function __construct($a) + { + $this->a = "hello"; + $this->foo2 = new Foo2(); + } +} + +$i = 0; +$j = 1; +$j = 2; +$k = []; + +$k = ['a' => ['b' => [1, 2, 3]]]; + +while (true) { + if ($i++ == 5) { + $k = ['hello' => 1, "world" => 1.1]; + $j = 5; + } + if ($i == 10) { + $k = ['hello', 'world']; + $j = 10; + break; + } +} + +echo 'hello' . PHP_EOL; + +require __DIR__ . '/header.php'; + +echo 'world' . PHP_EOL; + +$foo = new Foo(2); + +run(function () { + Coroutine::create(function () { + $i = 1; + test1(); + Coroutine::yield(); + $i = 2; + $a = ['a' => 1, 'b' => 2]; + var_dump($a); + }); + + Coroutine::create(function () { + $i = 3; + Coroutine::resume(2); + $i = 4; + }); +}); diff --git a/yasd.cc b/yasd.cc index 14dd271..449db8b 100644 --- a/yasd.cc +++ b/yasd.cc @@ -66,6 +66,8 @@ STD_PHP_INI_ENTRY("yasd.remote_host", "127.0.0.1", PHP_INI_ALL, OnUpdateString, remote_host, zend_yasd_globals, yasd_globals) STD_PHP_INI_ENTRY("yasd.remote_port", "9000", PHP_INI_ALL, OnUpdateLong, remote_port, zend_yasd_globals, yasd_globals) +STD_PHP_INI_ENTRY("yasd.depth", "1", PHP_INI_ALL, OnUpdateLong, + depth, zend_yasd_globals, yasd_globals) PHP_INI_END() // clang-format on