php里实现xml转数组,数组转xml的互转代码

近日MyQEE里加入了xml和数组互转的功能,调用代码分别是:

  • XML字符串转数组: Text::xml_to_array($xmlstr);
  • 数组转XML:Arr::to_xml($array);

其中XML转数组支持直接获取XML的URL转换,例如:

$arr = Text::xml_to_array('http://flash.weather.com.cn/wmaps/xml/china.xml');
print_r($arr)

XML代码示例:

<?xml version="1.0" encoding="UTF-8"?>
<TrafficEventResponse aa="true" bb="aaaa">
    <status>success</status>
    <currentCity aaa="aaaa">北京</currentCity>
    <test>true</test>
    <dateTime>2013/07/23/18/32/41</dateTime>
    <results>
        <result aa="11">
            <startTime>2013/03/08/10/51/00</startTime>
            <endTime>2013/10/08/12/00/00</endTime>
            <title ID="12" active="1" permission="13">东四西大街1车道限行(长期)</title>
            <description>东四西大街因为道路施工,导致1车道限行。</description>
            <location ID="12" active="1" permission="13">
                <lng>116.420497</lng>
                <lat>39.930511</lat>
                <root>
                    <folder ID="aaa" active="1" permission="13"><![CDATA[aaaaaaaaaaaaaaaaaaaa]]></folder>
                    <folder ID="bbb" active="1" permission="1"><![CDATA[bbbbbbbbbbbbbbbbbbbbbb]]></folder>
                </root>
            </location>
            <type>2</type>
        </result>
        <result aa="22">
            <startTime>2013/01/01/00/00/00</startTime>
            <endTime>2013/12/31/23/59/00</endTime>
            <title>清华东路3车道限行(长期)</title>
            <description>清华东路因为道路施工,导致3车道限行。</description>
            <location>
                <lng>116.37618</lng>
                <lat>40.007609</lat>
                <root>
                    <folder ID="ccc" active="1" permission="1"><![CDATA[ccccccccccccccccccc]]></folder>
                    <folder ID="ddd" active="1" permission="1"><![CDATA[dddddddddddddddddd]]></folder>
                </root>
            </location>
            <type>2</type>
        </result>
    </results>
</TrafficEventResponse>

转换成数组后:

Array
(
    [@name] => TrafficEventResponse
    [@attributes] => Array
        (
            [aa] => 1
            [bb] => aaaa
        )

    [status] => success
    [currentCity] => Array
        (
            [@attributes] => Array
                (
                    [aaa] => aaaa
                )

            [@data] => 北京
        )

    [test] => 1
    [dateTime] => 2013/07/23/18/32/41
    [results] => Array
        (
            [0] => Array
                (
                    [@name] => result
                    [@attributes] => Array
                        (
                            [aa] => 11
                        )

                    [startTime] => 2013/03/08/10/51/00
                    [endTime] => 2013/10/08/12/00/00
                    [title] => Array
                        (
                            [@attributes] => Array
                                (
                                    [ID] => 12
                                    [active] => 1
                                    [permission] => 13
                                )

                            [@data] => 东四西大街1车道限行(长期)
                        )

                    [description] => 东四西大街因为道路施工,导致1车道限行。
                    [location] => Array
                        (
                            [@attributes] => Array
                                (
                                    [ID] => 12
                                    [active] => 1
                                    [permission] => 13
                                )

                            [lng] => 116.420497
                            [lat] => 39.930511
                            [root] => Array
                                (
                                    [0] => Array
                                        (
                                            [@name] => folder
                                            [@attributes] => Array
                                                (
                                                    [ID] => aaa
                                                    [active] => 1
                                                    [permission] => 13
                                                )

                                            [@tdata] => aaaaaaaaaaaaaaaaaaaa
                                        )

                                    [1] => Array
                                        (
                                            [@name] => folder
                                            [@attributes] => Array
                                                (
                                                    [ID] => bbb
                                                    [active] => 1
                                                    [permission] => 1
                                                )

                                            [@tdata] => bbbbbbbbbbbbbbbbbbbbbb
                                        )

                                )

                        )

                    [type] => 2
                )

            [1] => Array
                (
                    [@name] => result
                    [@attributes] => Array
                        (
                            [aa] => 22
                        )

                    [startTime] => 2013/01/01/00/00/00
                    [endTime] => 2013/12/31/23/59/00
                    [title] => 清华东路3车道限行(长期)
                    [description] => 清华东路因为道路施工,导致3车道限行。
                    [location] => Array
                        (
                            [lng] => 116.37618
                            [lat] => 40.007609
                            [root] => Array
                                (
                                    [0] => Array
                                        (
                                            [@name] => folder
                                            [@attributes] => Array
                                                (
                                                    [ID] => ccc
                                                    [active] => 1
                                                    [permission] => 1
                                                )

                                            [@tdata] => ccccccccccccccccccc
                                        )

                                    [1] => Array
                                        (
                                            [@name] => folder
                                            [@attributes] => Array
                                                (
                                                    [ID] => ddd
                                                    [active] => 1
                                                    [permission] => 1
                                                )

                                            [@tdata] => dddddddddddddddddd
                                        )

                                )

                        )

                    [type] => 2
                )

        )

)

接下来是整理后的代码,完全独立使用

<?php
/**
 * 将一个XML字符串解析成一个数组
 *
 * 如果需要将数组转换成XML字符串,可使用 `array_to_xml($arr)` 方法
 *
 * ** 特殊的key **
 *
 *  key            | 说明
 * ----------------|-------------------------------
 *  `@attributes`  | XML里所有的 attributes 都存放在 `@attributes` key里,可自定义参数 `$attribute_key` 修改,设置成true则和标签里的内容合并
 *  `@name`        | 循环数组XML的标签(tag)存放在 `@name` 的key里
 *  `@tdata`       | CDATA内容存放在 `@tdata` 的key里
 *  `@data`        | 如果本来的值是字符串,但是又有 attributes,则内容被转移至 `@data` 的key里
 *
 *
 *     print_r(xml_to_array('http://flash.weather.com.cn/wmaps/xml/china.xml'));
 *
 * @param string|SimpleXMLElement $xml_string XML字符串,支持http的XML路径,接受 SimpleXMLElement 对象
 * @param string $attribute_key attributes所使用的key,默认 @attributes,设置成 true 则和内容自动合并
 * @param int $max_recursion_depth 解析最高层次,默认25
 * @return array | false 失败则返回false
 */

function xml_to_array($xml_string, $attribute_key = '@attributes', $max_recursion_depth = 25)
{
    if (is_string($xml_string))
    {
        if (preg_match('#^http(s)?://#i', $xml_string))
        {
            $xml_string = file_get_contents($xml_string);
        }
        $xml_object = simplexml_load_string($xml_string, 'SimpleXMLElement', LIBXML_NOCDATA);
    }
    elseif (is_object($xml_string) && $xml_string instanceof SimpleXMLElement)
    {
        $xml_object = $xml_string;
    }
    else
    {
        return false;
    }

    if (!$attribute_key)$attribute_key = '@attributes';
    if (null===$max_recursion_depth || false===$max_recursion_depth)$max_recursion_depth = 25;

    $format_attribute_value = function (& $tmp_value)
    {
        switch ($tmp_value)
        {
            case 'true':
                $tmp_value = true;
                break;
            case 'false':
                $tmp_value = false;
                break;
            case 'null':
                $tmp_value = null;
                break;
            default:
                $tmp_value = trim($tmp_value);
        }
    };

    $exec_xml_to_array = function ($xml_object, $attribute_key, $recursion_depth, $max_recursion_depth) use(&$exec_xml_to_array, $format_attribute_value)
    {
       

        /**
         * @var $xml_object SimpleXMLElement
         * @var $value SimpleXMLElement
         */

        $rs = array
        (
            '@name' => $xml_object->getName(),
        );

        $attr = get_object_vars($xml_object->attributes());

        if ($attr)
        {
            foreach($attr['@attributes'] as &$tmp_value)
            {
                $format_attribute_value($tmp_value);
            }
            unset($tmp_value);

            if (true===$attribute_key)
            {
                # 合并到一起
               $rs += $attr['@attributes'];
            }
            else
            {
                $rs[$attribute_key] = $attr['@attributes'];
            }
        }
        $tdata = trim("$xml_object");
        if (strlen($tdata)>0)
        {
            $rs['@tdata'] = $tdata;
        }

        $xml_object_var = get_object_vars($xml_object);

        foreach($xml_object as $key => $value)
        {
            $obj_value = $xml_object_var[$key];

            $attr = null;
            if (is_object($value))
            {
                $attr = get_object_vars($value->attributes());

                if ($attr)
                {
                    foreach($attr['@attributes'] as &$tmp_value)
                    {
                        $format_attribute_value($tmp_value);
                    }
                    unset($tmp_value);
                    $attr = $attr['@attributes'];
                }
            }

            if (is_string($obj_value))
            {
                $format_attribute_value($obj_value);

                if ($attr)
                {
                    if (true===$attribute_key)
                    {
                        # 合并到一起
                       $rs[$key] = $attr + array('@data' => $obj_value);
                    }
                    else
                    {
                        $rs[$key] = array
                        (
                            $attribute_key => $attr,
                            '@data'        => $obj_value,
                        );
                    }
                }
                else
                {
                    $rs[$key] = $obj_value;
                }
            }
            else
            {
                if (is_array($obj_value))
                {
                    if ($recursion_depth>0)unset($rs['@name']);
                    $rs[] = $exec_xml_to_array($value, $attribute_key, $recursion_depth+1, $max_recursion_depth);
                }
                else
                {
                    $rs[$key] = $exec_xml_to_array($value, $attribute_key, $recursion_depth+1, $max_recursion_depth);
                    if (is_array($rs[$key]) && !isset($rs[$key][0]))
                    {
                        unset($rs[$key]['@name']);
                    }
                }
            }
        }

        return $rs;
    };

    return $exec_xml_to_array($xml_object, $attribute_key, 0, $max_recursion_depth);
}



/**
 * 将数组转换成XML字符串
 *
 * 本方法的反向方法为 `xml_to_array($xml_string)`
 *
 *     // 返回格式化好的XML字符串
 *     array_to_xml($arr);
 *
 *     // XML缩进使用4个空格
 *     array_to_xml($arr, '    ');
 *
 *     // 返回不带任何换行符、空格的XML
 *     array_to_xml($arr, '', '');
 *
 * @param array $array 数组
 * @param string $tab 缩进字符,默认 tab 符
 * @param string $crlf 换行符,默认window换行符
 * @param string $attribute_key XML的attributes所在key,默认 `@attributes`
 * @param string $xml_header_string XML第一行声明的字符串
 * @return string
 */

function array_to_xml(array $array, $tab = "\t", $crlf = "\r\n", $attribute_key = '@attributes', $xml_header_string = null)
{
    if (!$xml_header_string)
    {
        $w = '?';
        $xml_header_string = '<'. $w . 'xml version="1.0" encoding="UTF-8"'. $w .'>';
    }

    $format_attribute_value = function (& $value)
    {
        if (true===$value)
        {
            $value = 'true';
        }
        elseif (false===$value)
        {
            $value = 'false';
        }
        elseif (null===$value)
        {
            $value = 'null';
        }
    };

    $format_to_xml_string = function($array, $attribute_key, $crlf, $tab, $left_str = '') use(&$format_to_xml_string, $format_attribute_value)
    {
        $str = '';

        if (isset($array['@name']))
        {
            $str .= "{$crlf}{$left_str}<{$array['@name']}";

            if (isset($array[$attribute_key]))
            {
                foreach($array[$attribute_key] as $k=>$v)
                {
                    $format_attribute_value($v);
                    $str .= " $k="{$v}"";
                }
            }

            $str .= ">";

            $close_str = "{$crlf}{$left_str}</{$array['@name']}>";

            $left_str .= $tab;
        }
        else
        {
            $close_str = '';
        }

        if (isset($array['@tdata']))
        {
            $str .= "<![CDATA[{$array['@tdata']}]]></{$array['@name']}>";
        }
        else
        {
            $have_str = false;
            foreach($array as $key => $value)
            {
                if ($key === '@name' || $key === $attribute_key || $key === '@data' || $key === '@tdata')continue;

                $have_str = true;

                if (is_array($value))
                {
                    if (!is_numeric($key))
                    {
                        $str .= "{$crlf}{$left_str}<{$key}";

                        if (isset($value[$attribute_key]))
                        {
                            foreach($value[$attribute_key] as $k=>$v)
                            {
                                $str .= " $k="{$v}"";
                            }
                        }
                        $str .= ">";

                        if (isset($value['@data']))
                        {
                            $format_attribute_value($value['@data']);
                            $str .= "{$value['@data']}";
                        }
                        elseif (isset($value['@tdata']))
                        {
                            $str .= "<![CDATA[{$value['@tdata']}]]>";
                        }
                        else
                        {
                            $tmp_str = $format_to_xml_string($value, $attribute_key, $crlf, $tab, $left_str . $tab);
                            if (''!==$tmp_str)
                            {
                                $str .= $tmp_str."{$crlf}{$left_str}";
                            }
                            unset($tmp_str);
                        }
                        $str .= "</{$key}>";
                    }
                    else
                    {
                        $str .= $format_to_xml_string($value, $attribute_key, $crlf, $tab, $left_str);
                    }
                }
                else
                {
                    $format_attribute_value($value);
                    $str .= "{$crlf}{$left_str}<{$key}>{$value}</{$key}>";
                }
            }

            if ($have_str)
            {
                if ($close_str)
                {
                    $str .= $close_str;
                }
            }
            else
            {
                $str .= "</{$array['@name']}>";
            }
        }

        return $str;
    };


    return $xml_header_string . $format_to_xml_string($array, $attribute_key, $crlf, $tab, '');
}






print_r($arr = xml_to_array('http://flash.weather.com.cn/wmaps/xml/china.xml'));

echo array_to_xml($arr);

使用ajax的streaming方式配合php的flush分块输出需要注意的地方

【背景】
ajax的Streaming方式是一种单向的push方式,与传统http输出不同的时,它是分块输出的,这样可以在一个较长的请求中随时向浏览器push一些内容。
之前我写过一个博客是介绍使用ajax的Streaming方式获取内容的,地址:http://www.queyang.com/blog/archives/510

【应用场景】
假设有一个ajax请求需要执行几十秒甚至几分钟,通常只有等待全部执行完毕才会得到结果,假如我们希望能够实时的看到程序的执行状态,比如进度,某个节点的错误或成功信息等,此时就可以利用此技术,做到实时观察程序运行状态。

【服务器程序】
对于PHP来说,支持这种分块输出是非常简单的,只要输出类似以下代码即可:

<?php
// 先输出1024长度内容,避免firefox等不识别
echo str_pad('', 1024), "\r\n";
flush();   // 将内容推送到浏览器

for($i=0; $i<10; $i++)
{
    echo 'step:'.$i.' time:'.time();
    flush();  //推送数据
    sleep(1);
}

但是如果用nginx做代理的话,需要注意把 proxy_buffering 设置成 off,nginx默认是开启的。
这个参数的具体说明地址在 http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering
它的用途是开启缓冲区,如果开启缓冲区则会导致请求在结束时才会一次性推送给浏览器,这样就达不到预想的效果了。

另外,需要说明的是,在MyQEE里默认是有缓冲区的,所以如果要使用flush实时把数据推送到浏览器,还需要把MyQEE的缓冲区关闭,关闭方法,在页面输出内容之前加入以下代码(只需运行一次即可):

Core::close_buffers(false);

V3全新SmartAdmin界面后台开放预览版下载

经过漫长的制作,全新的SmartAdmin界面的后台第一版预览版已经上线。

预览版演示地址:
http://myqeeadmin.sinaapp.com/

帐号:demo, 密码:123456

本后台是在SmartAdmin的界面基础上修改而来,使用了最新的 AngularJS 作为主框架,使用 requireJS 作为模块加载功能,CSS全部采用SCSS编写。

下载地址:
http://myqee-upload.stor.sinaapp.com/myqee/myqee-admin-preview.zip

【预览】

【使用说明】
首先将压缩包里的 admin.sql 导入到数据库中。

数据库链接配置,修改 team-library/config.php$config['database']['default'] 部分

站点配置,请配置在根目录下,比如 http://localhost/

【实时动态支持】
修改 php.ini 在最后加入 myqee.debug = On 保存,重启apache或nginx,这样就开启了MyQEE程序的debug功能
这样,程序静态资源都会请求 /assets-dev/*** 的地址,这个地址是通过PHP执行实时输出的,比如会把scss文件编译成css输出,这样就不用每次修改一个文件就要重新生成了
(这个也可以不加,不加的话,静态资源将使用 wwwroot/assets/p-default/~admin/ 中文件)

wwwroot/assets/p-default/~admin/ 中的文件都是进过编译输出的,【源文件】在 libraries/myqee/administration/assets/ 中,它编译会对应输出到 wwwroot/assets/p-default/~admin/ 目录中

【如何自行编译】
执行 bin/merge-assets 脚本,window下执行 bin/merge-assets.bat
比如 bin/merge-assets default –admin 即可
注意,需要 php, sass 支持

Apache配置例子(需apache开启rewrite模块):

<Virtualhost *:80>
    ServerName test.myqee.com
    DocumentRoot "~/Sites/myqee/"

    # not need edit
    DirectoryIndex index.html index.php
    RewriteEngine On
    RewriteRule .*/\..* - [F,L]
    RewriteCond %{DOCUMENT_ROOT}wwwroot%{REQUEST_FILENAME} -f [OR]
    RewriteCond %{DOCUMENT_ROOT}wwwroot%{REQUEST_FILENAME} -d
    RewriteRule ^/(.*)$ /wwwroot/$1 [PT,L]
    RewriteRule ^/.* /index.php [PT,L]
</Virtualhost>

MyQEE V3 RC2 版本发布

本次更新在3.0RC1的基础上做了一些完善,修复了一些Bug:

  • 修复了已知的大量Bug
  • 完善文档生成脚本
  • 完善模块化的拆分
  • 数据库增加对 group_concat 的支持,MongoDB数据库驱动支持在group查询中使用distinct查询,修复MongoDB驱动力中查询slave在新的版本里可能导致连接失败的问题
  • 增加 BigInt 类库
  • 日期类库完善
  • HttpClient 增加upload方法,可实现上传文件功能
  • 完善邮件类库,修复 Email 中上传附件bug,支持收件人姓名+邮件的格式,完善密件抄送的功能
  • Session 类库优化
  • Swift Storage 驱动完善,token验证支持v1和v2版本,优化参数传送方式,支持url方式的配置

HttpClient类库增加upload方法

HttpClient 类库将增加 upload 方法,目前可在3.1分支中获取,将同3.0正式版一起发布。

分支地址 https://github.com/breath-co2/myqee/tree/v3/3.1

可实现上传文件功能,使用方法:

HttpClient::factory()->upload('http://localhost/up.php', '/tmp/test.jpg');

其中 http://localhost/up.php 为接收上传文件的URL,/tmp/test.jpg 为待上次的文件路径

另外,upload 方法还支持同时post一些数据,比如:

HttpClient::factory()->upload('http://localhost/up.php', '/tmp/test.jpg', 'upload', array('a'=>1, 'b'=>2));

等同于

HttpClient::factory()
->add_file('/tmp/test.jpg', 'upload')
->post('http://localhost/up.php', array('a'=>1, 'b'=>2));

代码片段:返回一个目录的相对目录

/**
 * 求相对路径
 *
 *      $a = '/a/b/c/a.php';
 *      $b = '/a/b/e/b.php';
 *      echo self::relative_path($a, $b);
 *      // 将输出 ../../c/a.php
 *
 * @param string $a 目标路径
 * @param string $b 相对路径
 * @return string
 */

function relative_path($a, $b)
{
    $str = '';
    $a = explode('/', ltrim(str_replace('\\', '/', $a), '/'));
    $b = explode('/', ltrim(str_replace('\\', '/', $b), '/'));

    $intersect = array_intersect_assoc($a, $b);

    if($intersect)
    {
        $j   = -1;
        $num = 0;
        foreach ($intersect as $k => $v)
        {
            if($k-1 != $j)
            {
                break;
            }
            else
            {
                $str .= '..'. DIRECTORY_SEPARATOR;
            }

            $j = $k;
            $num++;
        }
        $ret = array_slice($a, $num);

        return $str . implode(DIRECTORY_SEPARATOR, $ret);
    }
}

调用方法:

$a = '/a/b/c/a.php';
$b = '/a/b/e/b.php';
echo relative_path($a, $b);
// 将输出 ../../c/a.php

走在云存储前沿,今日V3.1开发版本完善Swift Storage云存储接口,支持V2.0的Token接口

Swift Storage 是 openstack 开发的一套非常优秀的云存储方案,地址 http://www.openstack.org/software/openstack-storage/

通过它,你可以部署一个属于自己的云存储方案,它是基于key-value储存对象的。比如新浪SAE的云存储就是使用这个搭建的,具有非常稳定可靠的使用表现。

MyQEE 提供的 Storage 可以支持数据库驱动、文件驱动、Redis驱动以及Swift驱动,目前几乎还没有可以支持Swift协议的框架,迈启PHP框架通过驱动的方式扩展支持Swift协议,走在了云存储的前沿。

MyQEE紧跟Swift的脚本,继OpenStack提供相关的token V2.0接口后,MyQEE对Swift的驱动进行更新,目前已支持V2.0的Token接口。

Storage的使用方法:

$config = array
(
    'driver'        => 'Swift',
    'driver_config' => 'https://username:password@localhost:8080/v2.0?region=test&tenant_name=default&warehouses=mytest&prefix=test',
);
$storage = new Storage($config);

// 写入
$storage->set('test/123', 'hello');

// 读取
$storage->get('test/123');

以上代码也可以写成:

$config = array
(
    'driver' => 'Swift',
    'driver_config' => array
    (
        'host'              => 'localhost',     // 服务器IP或域名
        'user'              => 'username',      // 用户名
        'pass'              => 'password',      // 密码(key)
        'warehouses'        => 'mytest',        // 储存仓库,类似数据库的库,可不设置,默认为 default
        // 以下是token接口为v2.0的时候必须
        'tenant_name'       => 'default',       // Tenant 名称
        'region'            => 'test',          // Region
        // 以下为可选参数
        'https'             => true,            // true || false , 默认 false
        'token_api_version' => 'v2.0',          // 版本,不设置则默认 v2.0
        'port'              => 8080,            // 端口,默认http 为 80,https 为 443
        'prefix'            => 'test',          // key的前缀,默认为空
    ),
);
$storage = new Storage($config);

// 写入
$storage->set('test/123', 'hello');

// 读取
$storage->get('test/123');

MyQEE 全新首页上线, V3.0 RC1版本发布

经过1年的大版本调整,全新的V3.0 RC1版本已经发布,并且全新设计更新了官网。

这次更新经历了较漫长的过程,本来是打算发布V2.1版本的,但是在各位热心网友的提议下,直接更新到了3.0版本,并且在系统上相比有了较华丽的升级。

下面介绍下由2.0升级到3.0后带来的变化和优点:相比V2.0中增加了modules和drivers,在核心类库中把类似 DatabaseSessionSession 等大型类库都单独分离模块(即modules),并且目前已把 Database 的各个驱动分离到drivers里。这样做迎合了当前流行的模块化开发的趋势,模块化开发的好处就是可以最大限度的自定义。

更加注重系统安全:在V2的基础上,V3对系统内部调用加强了安全校验规则,并使用动态密码保证系统能够更加安全。增加了对 CSRF 攻击的防范。

另外,MyQEE也开始支持 Compress 包管理器安装和更新了,目前此功能还处于测试阶段,待正式版本发布后,你的程序可以直接用过包管理器进行升级了,再也不用担心自己的系统落后了。

PS: 由于后台类库还在升级中,所以RC1版本里不含目前官网的新版后台演示

以下是V3升级的一些列表

  • 核心部分改动及优化
    • 类库、模块、ORM、控制器等文件名命名实现全统一,全部改为小写方式
    • libraries/myqee/core类库改为根目录core/目录,并取消 $config['libraries']['core'] 的配置项,并增加DIR_CORE 常量,此常量目录为core目录
    • libraries/bootstrap.php 移动到core目录中,并修改index.php中include文件路径
    • 完善 Bootstrap::$base_url 的获取
    • 优化Session的读取,对生成的SessionID增加校验功能,防止伪造
    • 增加500错误数据收集的收集功能,可以关闭(配置中$config['error500']['close'] = true;即可)
    • 增加上传类库,并支持多驱动协议存放
    • Core::set_project($project) 改为 Core::change_project($project)
    • Session类库优化
    • ORM Finder中 get_by_id() 和 get_by_ids() 方法增加第二个参数$use_master指定是否在主库上查询,默认false
    • 特殊控制器目录修改,解决部分主机不支持特殊字符目录的问题
      • 后台控制器  controllers/[admin]/ 目录改为 controllers-admin/
      • 命令行控制器 controllers/[shell]/ 目录改为 controllers-shell/
      • 系统控制器  controllers/[system]/目录改为 controllers-system/
  • 控制器相关优化
    • 类命名优化,取消文件夹下双下划线命名方式,统一改成和class,model等相同的命名规则
    • 修复控制器参数大小BUG
    • 增加RESTFul的支持,RESTFul的控制器存放在 controllers-rest/ 目录,可使用 IS_REST_MODE 进行判断
    • 避免系统基础控制器可直接被访问到
    • 重写 Controller_Shell::getopt() 获取参数的代码,实现等同于系统的getopt()方法的功能
  • 数据库相关优化:
    • 增加慢查询日志功能,可在config.php中设置慢查询 $config['slow_query_mtime'] 参数
    • 各驱动优化,避免在连接数据库失败后将数据库用户名、密码等信息暴露到错误堆叠里
    • MongoDB优化主从模式查询,改Group查询为aggregate查询(MongoDB必需2.2版本以上,MongoDB扩展必需1.3+版本)
    • 查询where支持!=和<>,例如:$db->where('id!=', 3) 或 $db->where('id<>', 3); 表示查询条件为id不等于3
    • MySQL驱动支持distinct某个字段,例如 $db->distinct('test_field')
    • Database增加select_max()select_min()select_avg()select_sum() 等高级查询函数
    • 增加 set_builder($builder) 和 recovery_last_builder() 方法,适用与在多条查询中具有类似条件的情况
    • reset()方法支持重置单个信息,比如 $db->reset('select'); 可单独重置select的参数
    • 程序里支持$db = new Database('mysqli://root:123456@127.0.0.1/myqee/')这样的快速连接写法,省去数据库配置的麻烦
  • 代码开发相关优化
    • 将shell目录改名bin目录,增加 merge-assetsview-error500-log 等开发及部署工具
    • 优化Debug功能,支持Chrome浏览器下FirePHP功能
    • statics 控制器改为 assets/devmode/ 控制器,支持开发版本实时优化处理输出css(包括less)和js
  • Administration类库
    • 后台类库使用全新界面,视图使用bootstrap界面框架 详见 http://getbootstrap.com/
    • CSS采用less方式定义,方便修改
    • JS改为模块化,更方便二次开发
    • 支持手机版本
  • 其它
    • 语言包完善
    • 各种细节优化

MyQEE V2.0.1 正式版发布

修复2.0中发现的已知bug,比如:
HttpClient中错别字导致执行失败
文件缓存保存数据错误,文件处理类完善
完善了MySQL类库对编码的转换处理
完善了MongoDB的查询功能,完善了debug的显示
优化了Bootstrap中对debug的处理
完善了内部调用