Heim  >  Artikel  >  php教程  >  CI框架源码阅读笔记3 全局函数Common.php

CI框架源码阅读笔记3 全局函数Common.php

WBOY
WBOYOriginal
2016-06-06 19:36:03959Durchsuche

从本篇开始,将深入CI 框架 的内部,一步步去探索这个 框架 的实现、结构和设计。 Common.php文件定义了一系列的 全局 函数 (一般来说, 全局 函数 具有最高的加载优先权,因此大多数的 框架 中BootStrap引导文件都会最先引入 全局 函数 ,以便于之后的处理

  从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现、结构和设计。

  Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作)。

  打开Common.php中,第一行代码就非常诡异:

<span>if</span> ( ! <span>defined</span>('BASEPATH')) <span>exit</span>('No direct script access allowed');

CI框架源码阅读笔记2 一切的入口 index.php)中,我们已经知道,BASEPATH是在入口文件中定义的常量。这里做这个判断的原因是:避免直接访问文件,而必须由index.php入口文件进入。其实不仅是Common.php,System中所有文件,几乎都要引入这个常量的判断,避免直接的脚本访问:

CI框架源码阅读笔记3 全局函数Common.php

本文件中定义的函数如下(查看方式 print_r(get_defined_functions())):

 CI框架源码阅读笔记3 全局函数Common.php

CI中所有全局函数的定义方式都为:

<span>if</span> ( ! <span>function_exists</span>('func_name'<span>)){
    </span><span>function</span><span> func_name(){
     //function body
    }
}</span>

这样做,是为了防止定义重名函数(之后如果我们要定义系统的全局函数,也都将使用这种定义方式)。下面,一个个展开来看:

1.  is_php

这个函数的命名很明显,就是判断当前环境的PHP版本是否是特定的PHP版本(或者高于该版本)

函数内部有一个static的$_is_php数组变量,用于缓存结果(因为在特定的运行环境中,PHP的版本是已知的且是不变的,所以通过缓存的方式,避免每次调用时都去进行version_compare。这种方式,与一般的分布式缓存(如Redis)的处理思维是一致的,不同的是,这里是使用static数组的方式,而分布式缓存大多使用内存缓存)。

为什么要定义这个函数呢?这是因为,CI框架中有一些配置依赖于PHP的版本和行为(如magic_quotes,PHP 5.3版本之前,该特性用于指定是否开启转义,而PHP5.3之后,该特性已经被废弃)。这就好比是针对不同的浏览器进行Css Hack一样(这里仅仅是比喻,实际上,PHP并没有太多的兼容性问题)。

具体的实现源码:

<span>function</span> is_php(<span>$version</span> = '5.0.0'<span>)
{
    </span><span>static</span> <span>$_is_php</span><span>;
    </span><span>$version</span> = (<span>string</span>)<span>$version</span><span>;

    </span><span>if</span> ( ! <span>isset</span>(<span>$_is_php</span>[<span>$version</span><span>]))
    {
        </span><span>$_is_php</span>[<span>$version</span>] = (<span>version_compare</span>(<span>PHP_VERSION</span>, <span>$version</span>) FALSE : <span>TRUE</span><span>;
    }
    </span><span>return</span> <span>$_is_php</span>[<span>$version</span><span>];
}</span>

2.  is_really_writable

这个函数用于判断文件或者目录是否真实可写,一般情况下,通过内置函数is_writable()返回的结果是比较可靠的,但是也有一些例外,比如:

(a).    Windows中,如果对文件或者目录设置了只读属性,则is_writable返回结果是true,但是却无法写入。

(b).    Linux系统中,如果开启了Safe Mode,则也会影响is_writable的结果

因此,本函数的处理是:

  如果是一般的Linux系统且没有开启safe mode,则直接调用is_writable

否则:

  如果是目录,则尝试在目录中创建一个文件来检查目录是否可写

  如果是文件,则尝试以写入模式打开文件,如果无法打开,则返回false

注意,即使是使用fopen检查文件是否可写,也一定记得调用fclose关闭句柄,这是一个好的习惯。

函数的源码:

<span>function</span> is_really_writable(<span>$file</span><span>)
{
    </span><span>//</span><span> If we're on a Unix server with safe_mode off we call is_writable</span>
    <span>if</span> (DIRECTORY_SEPARATOR == '/' AND @<span>ini_get</span>("safe_mode") == <span>FALSE</span><span>)
    {
        </span><span>return</span> <span>is_writable</span>(<span>$file</span><span>);
    }

    </span><span>//</span><span> For windows servers and safe_mode "on" installations we'll actually write a file then read it</span>
    <span>if</span> (<span>is_dir</span>(<span>$file</span><span>))
    {
        </span><span>$file</span> = <span>rtrim</span>(<span>$file</span>, '/').'/'.<span>md5</span>(<span>mt_rand</span>(1,100).<span>mt_rand</span>(1,100<span>));

        </span><span>if</span> ((<span>$fp</span> = @<span>fopen</span>(<span>$file</span>, FOPEN_WRITE_CREATE)) === <span>FALSE</span><span>)
        {
            </span><span>return</span> <span>FALSE</span><span>;
        }

        </span><span>fclose</span>(<span>$fp</span><span>);
        @</span><span>chmod</span>(<span>$file</span>,<span> DIR_WRITE_MODE);
        @</span><span>unlink</span>(<span>$file</span><span>);
        </span><span>return</span> <span>TRUE</span><span>;
    }
    </span><span>elseif</span> ( ! <span>is_file</span>(<span>$file</span>) OR (<span>$fp</span> = @<span>fopen</span>(<span>$file</span>, FOPEN_WRITE_CREATE)) === <span>FALSE</span><span>)
    {
        </span><span>return</span> <span>FALSE</span><span>;
    }

    </span><span>fclose</span>(<span>$fp</span><span>);
    </span><span>return</span> <span>TRUE</span><span>;
}</span>

3.  load_class

这个函数有几个特殊的地方需要重点关注:

(1).    注意这个函数的签名,function &load_class( $class,$directory,$prefix).看到前面那个特殊的&符号没?没错,这个函数返回的是一个class实例的引用. 对该实例的任何改变,都会影响下一次函数调用的结果。

(2).    这个函数也有一个内部的static变量缓存已经加载的类的实例,实现方式类似于单例模式(Singleton)

(3).    函数优先查找APPPATH和BASEPATH中查找类,然后才从$directory中查找类,这意味着,如果directory中存在着同名的类(指除去前缀之后同名),CI加载的实际上是该扩展类。这也意味着,可以对CI的核心进行修改或者扩展。

下面是该函数的源码:

<span>function</span> &load_class(<span>$class</span>, <span>$directory</span> = 'libraries', <span>$prefix</span> = 'CI_'<span>)
{
    </span><span>/*</span><span> 缓存加载类的实例 </span><span>*/</span>
    <span>static</span> <span>$_classes</span> = <span>array</span><span>();
    </span><span>if</span> (<span>isset</span>(<span>$_classes</span>[<span>$class</span><span>]))
    {
        </span><span>return</span> <span>$_classes</span>[<span>$class</span><span>];
    }
    </span><span>$name</span> = <span>FALSE</span><span>;

    </span><span>/*</span><span> 先查找系统目录 </span><span>*/</span>
    <span>foreach</span> (<span>array</span>(APPPATH, BASEPATH) <span>as</span> <span>$path</span><span>)
    {
        </span><span>if</span> (<span>file_exists</span>(<span>$path</span>.<span>$directory</span>.'/'.<span>$class</span>.'.php'<span>))
        {
            </span><span>$name</span> = <span>$prefix</span>.<span>$class</span><span>;
            </span><span>if</span> (<span>class_exists</span>(<span>$name</span>) === <span>FALSE</span><span>)
            {
                </span><span>require</span>(<span>$path</span>.<span>$directory</span>.'/'.<span>$class</span>.'.php'<span>);
            }
            </span><span>break</span><span>;
        }

    }

    </span><span>/*</span><span>  查找之后并没有立即实例化,而是接着查找扩展目录 </span><span>*/</span>
    <span>if</span> (<span>file_exists</span>(APPPATH.<span>$directory</span>.'/'.config_item('subclass_prefix').<span>$class</span>.'.php'<span>))
    {
        </span><span>$name</span> = config_item('subclass_prefix').<span>$class</span><span>;
        </span><span>if</span> (<span>class_exists</span>(<span>$name</span>) === <span>FALSE</span><span>)
        {
            </span><span>require</span>(APPPATH.<span>$directory</span>.'/'.config_item('subclass_prefix').<span>$class</span>.'.php'<span>);

        }
    }

    </span><span>/*</span><span> 没有找到任何文件 </span><span>*/</span>
    <span>if</span> (<span>$name</span> === <span>FALSE</span><span>)
    {
        </span><span>exit</span>('Unable to locate the specified class: '.<span>$class</span>.'.php'<span>);
    }

    </span><span>/*</span><span>  将$class计入已加载的类列表  </span><span>*/</span><span>
    is_loaded(</span><span>$class</span><span>);

    </span><span>/*</span><span> 取得实例化 </span><span>*/</span>
    <span>$_classes</span>[<span>$class</span>] = <span>new</span> <span>$name</span><span>();

    </span><span>return</span> <span>$_classes</span>[<span>$class</span><span>];
}</span>

4.  is_loaded

这个函数用于追踪所有已加载的class。代码比较简洁,没有太多可讲的地方,这里直接贴出源码:

<span>function</span> &is_loaded(<span>$class</span> = ''<span>)
{
    </span><span>static</span> <span>$_is_loaded</span> = <span>array</span><span>();

    </span><span>if</span> (<span>$class</span> != ''<span>)
    {
       </span><span>$_is_loaded</span>[<span>strtolower</span>(<span>$class</span>)] = <span>$class</span><span>;
    }
    </span><span>return</span> <span>$_is_loaded</span><span>;
}</span>

5.  get_config

这个函数用于加载主配置文件(即位于config/目录下的config.php文件,如果定义了针对特定ENVIRONMENT的config.php文件,则是该文件)。该函数的签名为:

function &get_config($replace = array())

有几个需要注意的点:

(1).   函数只加载主配置文件,而不会加载其他配置文件(这意味着,如果你添加了其他的配置文件,在框架预备完毕之前,不会读取你的配置文件)。在Config组件实例化之前,所有读取主配置文件的工作都由该函数完成。

(2).   该函数支持动态运行的过程中修改Config.php中的条目(配置信息只可能修改一次,因为该函数也有static变量做缓存,若缓存存在,则直接返回配置)

(3). Return $_config[0] = & $config。是config文件中$config的引用,防止改变Config的配置之后,由于该函数的缓存原因,无法读取最新的配置。

这里还有一点无法理解,作者使用了$_config数组来缓存config,而只使用了$_config[0],那么问题来了,为什么不用单一变量代替,即:$_config = & $config; 如果有知道原因的童鞋,麻烦告知一声。

函数的实现源码:

<span>function</span> &get_config(<span>$replace</span> = <span>array</span><span>())
{
    </span><span>static</span> <span>$_config</span><span>;

    </span><span>if</span> (<span>isset</span>(<span>$_config</span><span>))
    {
        </span><span>return</span> <span>$_config</span>[0<span>];
    }

    </span><span>if</span> ( ! <span>defined</span>('ENVIRONMENT') OR ! <span>file_exists</span>(<span>$file_path</span> = APPPATH.'config/'.ENVIRONMENT.'/config.php'<span>))
    {
        </span><span>$file_path</span> = APPPATH.'config/config.php'<span>;
    }

    </span><span>if</span> ( ! <span>file_exists</span>(<span>$file_path</span><span>))
    {
        </span><span>exit</span>('The configuration file does not exist.'<span>);
    }

    </span><span>require</span>(<span>$file_path</span><span>);

    </span><span>if</span> ( ! <span>isset</span>(<span>$config</span>) OR ! <span>is_array</span>(<span>$config</span><span>))
    {
        </span><span>exit</span>('Your config file does not appear to be formatted correctly.'<span>);
    }

    </span><span>if</span> (<span>count</span>(<span>$replace</span>) > 0<span>)
    {
        </span><span>foreach</span> (<span>$replace</span> <span>as</span> <span>$key</span> => <span>$val</span><span>)
        {
            </span><span>if</span> (<span>isset</span>(<span>$config</span>[<span>$key</span><span>]))
            {
                </span><span>$config</span>[<span>$key</span>] = <span>$val</span><span>;
            }
        }
    }

    </span><span>return</span> <span>$_config</span>[0] =& <span>$config</span><span>;
}</span>

6.  config_item

这个函数调用了load_config,并获取相应的设置条目。代码比较简洁。不做过多的解释,同样只贴出源码:

<span>function</span> config_item(<span>$item</span><span>)
{
    </span><span>static</span> <span>$_config_item</span> = <span>array</span><span>();

    </span><span>if</span> ( ! <span>isset</span>(<span>$_config_item</span>[<span>$item</span><span>]))
    {
        </span><span>$config</span> =&<span> get_config();

        </span><span>if</span> ( ! <span>isset</span>(<span>$config</span>[<span>$item</span><span>]))
        {
            </span><span>return</span> <span>FALSE</span><span>;
        }
        </span><span>$_config_item</span>[<span>$item</span>] = <span>$config</span>[<span>$item</span><span>];
    }

    </span><span>return</span> <span>$_config_item</span>[<span>$item</span><span>];
}</span>

7.  show_error

 这是CI定义的可以用来展示错误信息的函数,该函数使用了Exceptions组件(之后我们将看到,CI中都是通过Exceptions组件来管理错误的)来处理错误。

 例如,我们可以在自己的应用程序控制器中调用该函数展示错误信息:

<span>Show_error(“trigger error info”);</span>

CI框架的错误输出还算是比较美观:

CI框架源码阅读笔记3 全局函数Common.php

注意该函数不仅仅是显示错误,而且会终止代码的执行(exit)

函数的源码:

<span>function</span> show_error(<span>$message</span>, <span>$status_code</span> = 500, <span>$heading</span> = 'An Error Was Encountered'<span>)
{
    </span><span>$_error</span> =& load_class('Exceptions', 'core'<span>);
    </span><span>echo</span> <span>$_error</span>->show_error(<span>$heading</span>, <span>$message</span>, 'error_general', <span>$status_code</span><span>);
    </span><span>exit</span><span>;
}</span>

8.  show_404

没有太多解释的东西,返回404页面

源码:

<span>function</span> show_404(<span>$page</span> = '', <span>$log_error</span> = <span>TRUE</span><span>)
{
    </span><span>$_error</span> =& load_class('Exceptions', 'core'<span>);
    </span><span>$_error</span>->show_404(<span>$page</span>, <span>$log_error</span><span>);
    </span><span>exit</span><span>;
}</span>

9.  log_message

调用Log组件记录log信息,类似Debug。需要注意的是,如果主配置文件中log_threshold被设置为0,则不会记录任何Log信息,该函数的源码:

<span>function</span> log_message(<span>$level</span> = 'error', <span>$message</span>, <span>$php_error</span> = <span>FALSE</span><span>)
{
    </span><span>static</span> <span>$_log</span><span>;

    </span><span>if</span> (config_item('log_threshold') == 0<span>)
    {
        </span><span>return</span><span>;
    }

    </span><span>$_log</span> =& load_class('Log'<span>);
    </span><span>$_log</span>->write_log(<span>$level</span>, <span>$message</span>, <span>$php_error</span><span>);
}</span>

10.  set_status_header

CI框架允许你设置HTTP协议的头信息(具体的HTTP状态码和对应含义可以参考:http://blog.csdn.net/ohmygirl/article/details/6922313)。设置方法为:

$this->output->set_status_header(“401”,“lalalala”);(CI的Output组件暴露了set_status_header()对外接口,该接口即是调用set_status_header函数)

值得注意的是,现在很多服务器内部扩展加入了自定义的状态码,如nginx:

ngx_string(ngx_http_error_495_page),   /* 495,<span> https certificate error */
ngx_string(ngx_http_error_496_page)</span>,   /* 496,<span> https no certificate */
ngx_string(ngx_http_error_497_page)</span>,   /* 497,<span> http to https */
ngx_string(ngx_http_error_404_page)</span>,   /* 498,<span> canceled */
ngx_null_string</span>,                       /* 499, client has closed connection */

所以你在查看服务器的error_log时,如果看到了比较诡异的错误状态码,不要惊慌,这不是bug. 这也说明,如果你要自定义自己的状态码和状态码描述文案,可以在该函数的内部$stati变量中添加自定义的状态码和文案。更多详细的内容,可以查看header函数的manual。

源码:

<span>function</span> set_status_header(<span>$code</span> = 200, <span>$text</span> = ''<span>)
{
    </span><span>/*</span><span> 所有的已定义状态码和描述文本 </span><span>*/</span>
    <span>$stati</span> = <span>array</span><span>(<br>     /* 2xx 成功 */
        </span>200    => 'OK',
        201    => 'Created',
        202    => 'Accepted',
        203    => 'Non-Authoritative Information',
        204    => 'No Content',
        205    => 'Reset Content',
        206    => 'Partial Content',
        /* 3xx 重定向 */    
        300    => 'Multiple Choices',
        301    => 'Moved Permanently',
        302    => 'Found',
        304    => 'Not Modified',
        305    => 'Use Proxy',
        307    => 'Temporary Redirect',
        /* 4xx 客户端错误 */
        400    => 'Bad Request',
        401    => 'Unauthorized',
        403    => 'Forbidden',
        404    => 'Not Found',
        405    => 'Method Not Allowed',
        406    => 'Not Acceptable',
        407    => 'Proxy Authentication Required',
        408    => 'Request Timeout',
        409    => 'Conflict',
        410    => 'Gone',
        411    => 'Length Required',
        412    => 'Precondition Failed',
        413    => 'Request Entity Too Large',
        414    => 'Request-URI Too Long',
        415    => 'Unsupported Media Type',
        416    => 'Requested Range Not Satisfiable',
        417    => 'Expectation Failed',
        /* 5xx 服务器端错误 */
        500    => 'Internal Server Error',
        501    => 'Not Implemented',
        502    => 'Bad Gateway',
        503    => 'Service Unavailable',
        504    => 'Gateway Timeout',
        505    => 'HTTP Version Not Supported'<span>
    );
    
    </span><span>/*</span><span> 状态码为空或者不是数字,直接抛出错误并退出 </span><span>*/</span>
    <span>if</span> (<span>$code</span> == '' OR ! <span>is_numeric</span>(<span>$code</span><span>))
    {
        show_error(</span>'Status codes must be numeric', 500<span>);
    }
    
    </span><span>if</span> (<span>isset</span>(<span>$stati</span>[<span>$code</span>]) AND <span>$text</span> == ''<span>)
    {
        </span><span>$text</span> = <span>$stati</span>[<span>$code</span><span>];
    }
    
    </span><span>/*</span><span> 设置的状态码不在已定义的数组中 </span><span>*/</span>
    <span>if</span> (<span>$text</span> == ''<span>)
    {
        show_error(</span>'No status text available.  Please check your status code number or supply your own message text.', 500<span>);
    }

    </span><span>$server_protocol</span> = (<span>isset</span>(<span>$_SERVER</span>['SERVER_PROTOCOL'])) ? <span>$_SERVER</span>['SERVER_PROTOCOL'] : <span>FALSE</span><span>;
    
    </span><span>/*</span><span> PHP以CGI模式运行 </span><span>*/</span>
    <span>if</span> (<span>substr</span>(<span>php_sapi_name</span>(), 0, 3) == 'cgi'<span>)
    {
        </span><span>header</span>("Status: {<span>$code</span>} {<span>$text</span>}", <span>TRUE</span><span>);
    }
    </span><span>elseif</span> (<span>$server_protocol</span> == 'HTTP/1.1' OR <span>$server_protocol</span> == 'HTTP/1.0')<span>/*</span><span> 检查HTTP协议 </span><span>*/</span><span>
    {
        </span><span>header</span>(<span>$server_protocol</span>." {<span>$code</span>} {<span>$text</span>}", <span>TRUE</span>, <span>$code</span><span>);
    }
    </span><span>else</span><span>
    {
        </span><span>header</span>("HTTP/1.1 {<span>$code</span>} {<span>$text</span>}", <span>TRUE</span>, <span>$code</span>);<span>/*</span><span>  默认为HTTP/1.1 </span><span>*/</span><span>
    }
}</span>

11.  _exception_handler

先看函数的签名:

function _exception_handler($severity, $message, $filepath, $line);

$ severity    :发生错误的错误码。整数

$message    :错误信息。

$filepath      :发生错误的文件

$line            :错误的行号

这个函数会根据当前设置的error_reporting的设置和配置文件中threshold的设置来决定PHP错误的显示和记录。在CI中,这个函数是作为set_error_handler的callback, 来代理和拦截PHP的错误信息(PHP手册中明确指出:以下级别的错误不能由用户定义的函数来处理<strong>E_ERROR</strong><strong>E_PARSE</strong><strong>E_CORE_ERROR</strong><strong>E_CORE_WARNING</strong><strong>E_COMPILE_ERROR</strong><strong>E_COMPILE_WARNING</strong>,和在 调用 set_error_handler() 函数所在文件中产生的大多数 <strong>E_STRICT </strong>。同样,如果在set_error_handler调用之前发生的错误,也无法被_exception_handler捕获,因为在这之前,_exception_handler尚未注册)。

再看源码实现:

<span>if</span> (<span>$severity</span> == <span>E_STRICT</span><span>){
    </span><span>return</span><span>;
}</span>

E_STRICT是PHP5中定义的错误级别,是严格语法模式的错误级别,并不包含在E_STRICT. 由于E_STRICT级别的错误可能会很多,因此,CI的做法是,忽略这类错误。

函数中实际处理和记录错误信息的是Exception组件:

<span>$_error</span> =& load_class('Exceptions', 'core');

然后根据当前的error_reporting设置,决定是显示错误(show_php_error)还是记录错误日志(log_exception):

<span>if</span> ((<span>$severity</span> & <span>error_reporting</span>()) == <span>$severity</span><span>)
{
    </span><span>$_error</span>->show_php_error(<span>$severity</span>, <span>$message</span>, <span>$filepath</span>, <span>$line</span><span>);
}</span>

注意,这里是位运算&而不是逻辑运算&&, 由于PHP中定义的错误常量都是整数,而且是2的整数幂(如

  1       E_ERROR

  2       E_WARNING

  4       E_PARSE

  8       E_NOTICE        

  16     E_CORE_ERROR

  ...

),因此可以用&方便判断指定的错误级别是否被设置,而在设置的时候,可以通过|运算:

<span>/*</span><span> 显示E_ERROR,E_WARNING,E_PARSE错误 </span><span>*/</span>
<span>error_reporting</span>(<span>E_ERROR</span> | <span>E_WARNING</span> | <span>E_PARSE</span><span>);

</span><span>/*</span><span> 显示除了E_NOTICE之外的错误 </span><span>*/</span>
<span>error_reporting</span>(<span>E_ALL</span> & ~<span>E_NOTICE</span> | E_STRICE);

这与Linux的权限设置rwx的设计思想是一致的(r:4  w:2  x:1)

有时候仅仅显示错误是不够的,还需要记录错误信息到文件:

如果主配置文件config.php中$config['log_threshold'] == 0,则不记录到文件:

<span>if</span> (config_item('log_threshold') == 0<span>)
{
    </span><span>return</span><span>;
}</span>

否者,记录错误信息到文件(这之中,调用组件Exception去写文件,Exception组件中会调用log_message函数,最终通过Log组件记录错误信息到文件。模块化的一个最大特点是每个组件都负责专门的职责,而模块可能还会暴露接口被其他组件调用。)

最后,贴上完整的源码:

<span>function</span> _exception_handler(<span>$severity</span>, <span>$message</span>, <span>$filepath</span>, <span>$line</span><span>)
{
    </span><span>if</span> (<span>$severity</span> == <span>E_STRICT</span><span>)
    {
               </span><span>return</span><span>;
    }
    </span><span>$_error</span> =& load_class('Exceptions', 'core'<span>);

    </span><span>if</span> ((<span>$severity</span> & <span>error_reporting</span>()) == <span>$severity</span><span>)
    {
               </span><span>$_error</span>->show_php_error(<span>$severity</span>, <span>$message</span>, <span>$filepath</span>, <span>$line</span><span>);
    }
    </span><span>if</span> (config_item('log_threshold') == 0<span>)
    {
               </span><span>return</span><span>;
    }
    </span><span>$_error</span>->log_exception(<span>$severity</span>, <span>$message</span>, <span>$filepath</span>, <span>$line</span><span>);
}</span>

12.  Remove_invisiable_character

这个函数的含义非常明确,就是去除字符串中的不可见字符。这些不可见字符包括:

ASCII码表中的00-31,127(保留09,10,13,分别为tab,换行和回车换行,这些虽然不可见,但却是格式控制字符)。然后通过正则替换去除不可见字符:

<span>do</span><span>{
    </span><span>$str</span> = <span>preg_replace</span>(<span>$non_displayables</span>, '', <span>$str</span>, -1, <span>$count</span><span>);
}
</span><span>while</span> (<span>$count</span>);

理论上将,preg_replace会替换所有的满足正则表达式的部分,这里使用while循环的理由是:可以去除嵌套的不可见字符。如  %%0b0c。如果只执行一次替换的话,剩余的部分%0c依然是不可见字符,所以要迭代去除($count返回替换的次数)。

完整的函数源码:

<span>function</span> remove_invisible_characters(<span>$str</span>, <span>$url_encoded</span> = <span>TRUE</span><span>)
{
    </span><span>$non_displayables</span> = <span>array</span><span>();

    </span><span>if</span> (<span>$url_encoded</span><span>)
    {
        </span><span>$non_displayables</span>[] = '/%0[0-8bcef]/';    <span>//</span><span> url encoded 00-08, 11, 12, 14, 15</span>
        <span>$non_displayables</span>[] = '/%1[0-9a-f]/';       <span>//</span><span> url encoded 16-31</span>
<span>    }       

    </span><span>$non_displayables</span>[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';     <span>//</span><span> 00-08, 11, 12, 14-31, 127</span>

    <span>do</span><span>
    {
        </span><span>$str</span> = <span>preg_replace</span>(<span>$non_displayables</span>, '', <span>$str</span>, -1, <span>$count</span><span>);
    }</span><span>while</span> (<span>$count</span><span>);
    
    </span><span>return</span> <span>$str</span><span>;
}</span>

13.  Html_escape

这个函数,实际上是数组中的元素递归调用htmlspecialchars。

函数实现源码:

<span>function</span> html_escape(<span>$var</span><span>)
{
    </span><span>if</span> (<span>is_array</span>(<span>$var</span><span>))
    {
        </span><span>return</span> <span>array_map</span>('html_escape', <span>$var</span><span>);
    }
    </span><span>else</span><span>
    {
        </span><span>return</span> <span>htmlspecialchars</span>(<span>$var</span>, ENT_QUOTES, config_item('charset'<span>));
    }
}</span>

总结一下,Common.php是在各组件加载之前定义的一系列全局函数。这些全局函数的作用是获取配置、跟踪加载class、安全性过滤等。而这么做的目的之一,就是避免组件之间的过多依赖。

参考文献:

PHP引用:http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/09/10/2173092.html

HTTP协议:http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html

单例模式:http://cantellow.iteye.com/blog/838473

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn