Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swoole+runkit7_import have error #234

Open
chenpinzhong opened this issue Sep 8, 2020 · 5 comments
Open

swoole+runkit7_import have error #234

chenpinzhong opened this issue Sep 8, 2020 · 5 comments
Labels

Comments

@chenpinzhong
Copy link

chenpinzhong commented Sep 8, 2020

For bug reports, please include:

  1. php --version (full output, including NTS/ZTS)
    php -v
    PHP 7.4.9 (cli) (built: Sep 7 2020 15:23:27) ( NTS )
    Copyright (c) The PHP Group
    Zend Engine v3.4.0, Copyright (c) Zend Technologies

  2. Runkit7 version (echo phpversion('runkit7');). Make sure that the bug can be reproduced in the latest version on https://pecl.php.net/package/runkit7 (or newer)
    runkit7 version 4.0.0dev

  3. OS version and compiler version
    gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
    CentOS Linux release 7.7.1908 (Core)

  4. If possible, a minimal, self-contained snippet reproducing the problem,
    or a link to the affected project and steps to reproduce the issue.

        ///////////////////////////////////////////////////
        //命令行模式运行
        $http = new Swoole\Http\Server("0.0.0.0", 8080);
        //\Swoole\Http\Response->gzip(5);
        $http->set([
            'tcp_fastopen' => true,
            'request_slowlog_timeout' => 5, // 设置请求超时时间为5秒
            'request_slowlog_file' => '/tmp/trace.log',
            'trace_event_worker' => true, //跟踪 Task 和 Worker 进程
            'hook_flags' => SWOOLE_HOOK_SLEEP,
            'buffer_output_size' => 32 * 1024 *1024, //最大数据发送的大小
        ]);
        $http->set(['enable_static_handler' => true,'package_max_length' => 1024*1024*1000]);
        $http->on('request', function ($request, $response){
            global $http_server;//引用处理类
            $http_server->swoole_handle($request,$response);//处理请求
        });
        $http->start();

    //swoole_handle 处理请求 (更好的并发性能)
    public function swoole_handle($swoole_request, $swoole_response){
        global $request;//模板引擎
        $this->swoole_request = $swoole_request;
        $this->swoole_response = $swoole_response;
		$http_data = "";//响应的html数据
		$request->swoole_request_analyze($swoole_request);
		////////////////////////////////////////////////////////
		//获取请求信息
		$module_name = $request->param('module');//模块
		$controller_name = $request->param('controller');//控制器
		$function_name = $request->param('function');//方法

		if (empty($module_name)) $module_name = 'index';//默认模块名称
		if (empty($controller_name)) $controller_name = 'index';//默认控制器名称
		if (empty($function_name)) $function_name = 'index';//默认方法名称

		$class_name = $controller_name;//类名称=控制器名称
		$controller_file = APP . DS . $module_name . DS . controller . DS . $controller_name . '.php';//得到控制器路径
		$view_file = APP . DS . $module_name . DS . view . DS . $controller_name . DS . $function_name . '.html';//模板文件

		//判断文件是否存在
		if (file_exists($controller_file)) {
			//clearstatcache();
			//$fstat = stat($controller_file);
			runkit7_import($controller_file, RUNKIT_IMPORT_CLASS_METHODS|RUNKIT_IMPORT_OVERRIDE);//覆盖导入类
			$class_namespacename = "\\" . $module_name . "\\".controller."\\" . $class_name;//模块,控制器,类名称

			$class_obj = new $class_namespacename();
			if (method_exists($class_obj, $function_name)) {
				$http_data=$class_obj->$function_name();
			} else {
				$http_data = "模块/控制器/方法->" . $module_name . '/' . $controller_name . '/' . $function_name . "不存在!";
			}

			//判断试图文件是否存在 存在则使用试图
			if(empty($http_data) && file_exists($view_file)){
				$http_data = $smarty->fetch($view_file);
			}
			$this->swoole_response->header("Content-Type", "text/html; charset=utf-8");
			$this->swoole_response->end($http_data);
			unset($class_obj);//使用完成后释放对象
		} else {
			$this->swoole_response->header("Content-Type", "text/html; charset=utf-8");
			$http_data = '<h1 style="text-align: center; margin: 50px;">404 文件丢失</h1>';
			$this->swoole_response->end($http_data);
		}
	}

///////////////////////////////////////////////////////////////////
//  Errors that occur 发生的错误
WARNING check_worker_exit_status: worker#1[pid=12266] abnormal exit, status=0, signal=11
A bug occurred in Swoole-v4.5.3, please report it.
The Swoole developers probably don’t know about it,
and unless you report it, chances are it won’t be fixed.
You can read How to report a bug doc before submitting any bug reports:


//Normal test code 正常测试代码

test_file.php 文件1
namespace index\controller;
class index{
    public function index(){
        return json_encode(array("a11"=>"333"));
    }
}

test_file2.php 文件2
namespace index\controller;
class index{
    public function index(){
        return json_encode(array("a11"=>"3334444"));
    }
}

test.php
//测试程序代码
require "test_file.php";
$index=new index\controller\index();
echo $index->index();
$controller_file= "test_file2.php";
runkit7_import($controller_file, RUNKIT_IMPORT_CLASS_METHODS|RUNKIT_IMPORT_OVERRIDE);//覆盖导入类ss
$index=new index\controller\index();
echo $index->index();
//输出结果 {“a11”:”333”}{“a11”:”3334444”}
//通过
@TysonAndre
Copy link
Member

Related to #150 - Crashes in php 7.3+ are known

https://github.com/runkit7/runkit7#runkit7-independent-fork-of-runkit-for-php-72 documents this

Note that PHP 7.3+ has known crashes in runkit7_import() but all other functionality works.)

I spent hours looking into it then - the internals of php changed quite a bit since then, e.g. how PHP's internals for late static binding worked, and there didn't seem to be a straightforward way to get it to work, and the cause of any crashes was hard to determine from where crashes actually happened. Memory management of classes which were imported in a different context seemed to have changed and would no longer be straightforward

One of the causes of crashes is importing a class that has an ancestor that was loaded in the caller. That used to work in php 7.2 reliably, but crashes in 7.4, probably due to the late static binding changes.

Copying and pasting large parts of php's internals for selectively importing classes and functions might work, but wouldn't be maintainable and would probably introduce different bugs.

@TysonAndre TysonAndre added the bug label Sep 8, 2020
TysonAndre added a commit that referenced this issue Sep 8, 2020
There are various known crashes in runkit7_import
due to overrides, extending classes that were already loaded,
late static binding, etc.

Related to #234, #211, #185, #150, #135, #112

Closes #72
TysonAndre added a commit that referenced this issue Sep 8, 2020
There are various known crashes in runkit7_import
due to overrides, extending classes that were already loaded,
late static binding, etc.

Fixing this would probably require copying large amounts of php's
functionality for php 7.3, 7.4, 8.0, etc.,
and would likely be a massive undertaking.
Crashes and test failures often occur far away from the buggy code
causing the crash.

Related to #234, #211, #185, #150, #135, #112

Closes #72
@chenpinzhong
Copy link
Author

chenpinzhong commented Sep 9, 2020

I need this runkit7_import function, because it can update the business logic without restarting the service!
Generally, development is only to modify the business module, and rarely touch the core functions.
Expect PHP to solve these problems better

I was wondering if it is possible to delete the classes with the same namespace, and then the new introduction can continue to work without causing PHP errors

我非需要这个runkit7_import功能,因为这样可以在不重启服务就能热更新业务逻辑!
一般开发也只是在业务模块里面修修改改 很少会碰核心功能
期待PHP 会更好的解决这些问题

我在想是否可以,把命名空间相同的类删除掉,然后新的引入不引发PHP错误就可以继续工作

@TysonAndre
Copy link
Member

TysonAndre commented Sep 9, 2020

I was wondering if it is possible to delete the classes with the same namespace, and then the new introduction can continue to work without causing PHP errors

That was proposed in #29 - my response was #29 (comment) - the constraints on when it would be possible are too unpredictable for me to think it would be useful.

There's https://www.php.net/manual/en/function.uopz-set-mock.php in a different extension but I don't know if it'd conflict with runkit7 or other extensions you'd have installed (xdebug, newrelic, etc). It might not even meet your use case.

  • Also, uopz introduces a performance overhead and certain ways of using it may lead to different issues

@chenpinzhong
Copy link
Author

Check if modification date of file changed, return if it didn't. (Probably need to call clearstatcache($pathToFile) first)
$source = file_get_contents('path/to/MyClass.php'); // or stream_get_include_path
$hash = md5($source);
$genClassName = 'MyClass_' . $hash; // use variable $origClassName instead
if (class_exists($genClassName, false)) { return; }
$source = preg_replace('/\bMyClass\b/i', $genClassName, $source);
eval($source);
return new $genClassName(args);

I also thought of this method but didn’t implement it
But it should be a good solution for now

我也想到了 这种方法 但是没有去实现它
但是 目前来说应该是个不错的解决方案

@YonderChen
Copy link

I hope to change the public field of the class to protected at runtime, so that field access and setting will to be called by magic method "__get" and "__set". I don't know if there is any better implementation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants