__autoload 内部实现原理

__autoload  

官方:尝试加载未定义的类。我的理解是在New 对象的时候,会将对象名传递。

<?php
function __autoload($class_name) { 
    echo '加载的类名:'.$class_name;
} 

@$a = new iwonmo(); 
@$a = new iwonmo1();

运行结果输出:加载的类名:iwonmo

当New的类不存在时,可以通过这个函数进行include/require。

不过注意官方的警告:This feature has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged. 在7.2.0以后的版本被弃用

SPL

SPL的全称是:Standard PHP Library (PHP标准库),版本:PHP 5 >= 5.1.0, PHP 7,这个库是做什么用的,在官网也有解释,只不过需要看英文版,因为中文版的介绍实际是不全的,不单单是这个解释。所以看PHP的文档尽量看英文版的。

The Standard PHP Library (SPL) is a collection of interfaces and classes that are meant to solve common problems.

No external libraries are needed to build this extension and it is available and compiled by default in PHP 5.0.0.

SPL provides a set of standard datastructure, a set of iterators to traverse over objects, a set of interfaces, a set of standard Exceptions, a number of classes to work with files and it provides a set of functions like spl_autoload_register()


大致的意思是SPL是一个解决常见问题接口或者的集合。然后里面提供了一组叫做SPL的函数。另外(英文里不包含):SPL函数也取代了__autoload。

spl_autoload_register的官方解释里有这么一句话:如果在你的程序中已经实现了__autoload()函数,它必须显式注册(explicitly registered )到__autoload()队列中。因为 spl_autoload_register()函数会将Zend Engine中的__autoload()函数取代为spl_autoload()或spl_autoload_call()。

对于这组函数可以翻阅PHP的源代码(php_spl.c):

int spl_autoload(const char *class_name, const char * lc_name, int class_name_len, const char * file_extension TSRMLS_DC) /* {{{ */
{
    char *class_file;
    int class_file_len;
    int dummy = 1;
    zend_file_handle file_handle;
    zend_op_array *new_op_array;
    zval *result = NULL;

    class_file_len = spprintf(&class_file, 0, "%s%s", lc_name, file_extension);

    if (zend_stream_open(class_file, &file_handle TSRMLS_CC) == SUCCESS) {
        if (!file_handle.opened_path) {
            file_handle.opened_path = estrndup(class_file, class_file_len);
        }
        if (zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL)==SUCCESS) {
            new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);
            zend_destroy_file_handle(&file_handle TSRMLS_CC);
        } else {
            new_op_array = NULL;
            zend_file_handle_dtor(&file_handle);
        }
        if (new_op_array) {
            EG(return_value_ptr_ptr) = &result;
            EG(active_op_array) = new_op_array;
    
            zend_execute(new_op_array TSRMLS_CC);
    
            destroy_op_array(new_op_array TSRMLS_CC);
            efree(new_op_array);
            if (!EG(exception)) {
                if (EG(return_value_ptr_ptr)) {
                    zval_ptr_dtor(EG(return_value_ptr_ptr));
                }
            }

            efree(class_file);
            return zend_hash_exists(EG(class_table), (char*)lc_name, class_name_len+1);
        }
    }
    efree(class_file);
    return 0;
} /* }}} */

大致就是找到这个文件,然后编译成OP代码,然后执行,释放。实际这个过程也是__autoload的过程。这里有一个地方是会将对象名转换为小写,不知道这段代码的意义。不过明白大致意思就可以。file_extension由于被宏定义,所以这里它是一个上下文。


个人理解,如有错误还望指出。

七牛 大文件上传

大文件报错:invalid multipart format: multipart: message too large

UploadManager->put 改为 UploadManager->putFile


原生与WebView传递超大文件

方法一

原生实现 分块读取接口,由JS去调用,读取本地文件。

方法二

将大文件转成特定格式如base64,以对象形式存储生成JS文件,动态加载JS文件,调用相应接口,获取base64数据。

js文件如下

function load(){
    var ob = new Object(); /** 这里内部会创建一个Handle<JSOBJECT>的对象 是一个地址 */
    ob.data="亿万数据"; /** 由于是内存地址所以可以存储小于当前内存的一块空间,理论上无限大 */
    return ob; /** 返回这一个对象 */
}

原生可以将文件base64写入到手机,然后webview的JS通过动态加载JS获取load函数,从而调用load函数实现得到大文件的数据。

调用方法(jQuery)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <button style="width:100px;height:30px;" onclick="getJs()">获取</button>
    <script>
        function getJs(){
            $.getScript("hotLoad.js",(e)=>{
                console.log(load());
            });
        };
    </script>
</body>
</html>

由于getScript是Ajax实现的,所以注意跨域问题。使用其他方式实现JS的加载也可。