仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1968|回复: 18
打印 上一主题 下一主题

[学习教程] PHP网页设计一次编写,到处运转

[复制链接]
柔情似水 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-2-16 00:29:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
第1步环境配置好了,你算了进了1小步了,那么第2步呢 就是调出第1个程序 一般都是用hello world,视频教程里面我都做了,hello world   关于 PHP 的数据库 API 关于分歧的数据库有分歧的函数,一向以来就有人测验考试利用 PHP 的面向对象的功效停止一些封装。个中有对照有名的ADODB,PHPLIB。后来环球注目的 PEAR 项目中的 PEAR DB 更是个中的佼佼者。这些用面向对象对数据库 API 停止的封装的包普通称为数据库笼统层。

本文引见的是 PEAR 中对 PEAR DB 停止交融 Metabase 库的一些优异功效以后发生的重视效力,并且复杂易用,功效十分壮大的 MDB 的一个十分好的引见。作者就是 MDB 的主创人员。

想取得我比来存眷的 PHP/PEAR 的最新原创和译文,请会见我的主页

Write once - run anywhere
一次编写??到处运转

PEAR MDB Database Abstraction Layer
PEAR MDB 数据库笼统层

作者:Lukas Smith
译者:taowen

While this is a Java marketing phrase it is also a key feature of PHP. Many business models depend on operation system independence to ensure that products can be sold to a wide range of customers. So why lock yourself in with a specific database vendor? Database abstraction layers allow you to develop your application independent of a database. But often they eat more performance than you are willing to give or they do not abstract enough to eliminate all database specific code.
这是Java的一句行销标语,然而它同时也是PHP的关头特征之一。很多贸易模子依附于操作体系有关性来包管产物可以发卖给普遍的客户群体。因此,为何要把你本人绑在某种数据库厂商的身上呢?数据库笼统层使得你可以与数据库自力的开辟你的使用法式。然而,凡是情形下它们对功能的影响超越了你所但愿的,要末他们其实不足够笼统以消弭一切和特定命据库相干的代码。

What will this article teach me?
这篇文章将教给我甚么?

This article will give a good introduction to the database abstraction package PEAR MDB. The focus will be explaining the more advanced features of MDB like data type abstraction and the XML based schema management that go beyond what other similar packages offer. A basic level of understanding of PHP and SQL is recommended.
这篇文章将对数据库笼统包 PEAR MDB 有一个很好的引见。文章的核心将是对 MDB 超出相似包所供应的更先辈的特征,例如数据类型笼统和基于 XML 的 schema 办理。对 PHP 和 SQL 的根基了解是保举的。

Why another database class?
为何别的再要一个数据库类?

Often, web projects are added to existing IT infrastructures, where the client already made a choice of what RDBMS (relational database management system) to work with. Even if that is not the case different budgets might affect what database you chose for deployment. Finally, you as the developer simply might prefer not to lock yourself in with a specific vendor. So far this meant to keep multiple versions for each supported database or giving up more performance and ease of use than necessary: Enter PEAR MDB.
凡是, web 工程在客户已肯定了要利用那种 RDBMS (关系型数据库办理体系)以后被添加给已存在的 IT 基本布局。即便那并非由于分歧的预算能够影响的你选择何种数据用于安排的情形。终究,你作为开辟者能够复杂的偏好过不把本人绑在某个厂商身上。自此,意味着给每一个撑持的数据坚持版本或就义更多功能然而取得多于必需的易用性:走入 PEAR MDB 吧。

MDB is a database abstraction layer that aims to make writing RDBMS independent PHP application development a straightforward process. Most other so called database abstraction layers for PHP only provide a common API for all supported databases and only very limited abstraction (mostly only for sequences). MDB on the other hand can be used to abstract all data being send and received from the database. Even database schemas can be defined in a RDBMS independent format. But it does this while retaining a high level of performance and ease of use. This was achieved by closely examining two popular database abstraction layers, PEAR DB and Metabase, and merging them. But during the merging the opportunity was also used to clean up their merged APIs as well as any performance hindering design patterns.
MDB 是着眼于使得编写 RDBMS 有关的 PHP 法式成为复杂的进程的数据库笼统层。大局部其他的 PHP 的所谓数据库笼统层牢牢给一切撑持的数据库供应了一个公用 API 和十分无限的笼统(大局部只是针对序列的)。MDB 另外一方面可以用来笼统一切数据库发送和吸收的数据。乃至数据库 schema 都能被界说为 RDBMS 有关的格局。然而它供应这些功效的同时依然坚持了很高的功能和复杂易用。这是经由过程深切察看两个盛行的数据库笼统层,PEAR DB 和 Metabase, 以后而且对它们停止了交融后取得的。并且在交融过程当中,趁着这个时机清算了它们交融后的 API 和任何影响功能的设计。

How did MDB come to be?
MDB 是如何呈现的?

Back in fall 2001, I was looking for a database abstraction package that would make my companies application framework RDBMS independent. The goal was to reduce database specific code to zero. The only package I found that offered such features was Metabase. But Metabase had a somewhat uncomfortable API that was partly a result of the compatibility to PHP3. This also made Metabase slower than it needed to be for our purposes, since we did not need PHP3 compatibility. Nonetheless, we decided that Metabase is our only option. But even after adding a performance enhancing patch to Metabase we felt that we were giving up too much performance. We met with the author of Metabase at the International PHP Conference 2001 and we talked about the benefits of having something like Metabase as part of the PEAR project. Shortly afterwards a discussion began once more in the PEAR mailing list about the potential benefits of a merge of PEAR DB and Metabase. After much discussion at my company we decided to take up this task. After several months of hard work we now have the first stable release of MDB.
早在 2001 年的秋季,我就在寻觅一种能够可以让我公司的法式框架与 RDBMS 自力的数据库笼统包。这个方针是把特定命据库相干的代码数目削减到零。我发明供应如许的功效的独一的一个包是 Metabase。然而 Metabase有一些局部是由于为了和 PHP3 兼容的让人不恬逸的 API。虽然如斯,咱们决意 Metabase 是咱们独一的选择。然而即便是在给 Metabase 增添了一特性能改善的补钉以后,咱们依然感应咱们保持了太多的功能。咱们在 2001 年的 PHP 国际会议上碰着了 Metabase 的作者,而且咱们议论了让像 Metabase 如许的器材成为 PEAR 工程一局部的优点。后来不久,在 PEAR 邮件列表上就 PEAR DB 和 Metabase 交融的能够的优点又入手下手了一场会商。在咱们公司停止了很多会商以后,咱们决意承当这个义务。数个月的艰苦任务以后,咱们如今有了 MDB 的第一个不乱的 release。

What does MDB do for you?
MDB 给你供应了甚么?

MDB combines most of the features of PEAR DB and Metabase. Actually, the only feature that is missing is PEAR DB's feature of returning an object as a result set. This feature was dropped because the feature's usage never became abundant but the performance penalty was quite apparent. A lot of development time was spend on making the API as intuitive as possible as well. Finally, MDB provides this functionality at a very high level of performance that is at least as fast as PEAR DB and much faster than Metabase. Here is the list of the most important features:
MDB 联合了 PEAR DB 和 Metabase 的大局部特征。实践上,PEAR DB 的特征中独一不再存在的是作为了局集前往一个对象。咱们保持了这个特征是由于这个特征不经常使用并且关于功能的丧失长短常分明的。很多开辟上的工夫用在了使得 API 尽量的好用。终究,MDB 十分洼地供应了这些功效最少和 PEAR DB 一样快并且比 Metabase 快良多。这些最主要地特征的列表:

OO-style API
prepared queries emulation
full data type abstraction for all data passed to and from the database (including LOB support)
transaction support
database/table/index/sequence creation/dropping/altering
RDBMS independent database schema management
Integrated into the PEAR framework (PEAR Installer, PEAR error handling etc.)

OO 作风的 API
预筹办的查询摹拟
给一切传递出去和从数据库中掏出的数据的完整的数据类型笼统(包含 LOB 撑持)
事务撑持
数据库/表/索引/序列创立/丢弃/改动
RDBMS 有关的数据库 schema 办理
承继进 PEAR 框架(PEAR 装置法式,PEAR 毛病处置等)

So how does it work?
那末它若何利用呢?

MDB provides some very advanced abstraction features. It is important to keep in mind that these features are optional. But using them is critical in writing RDBMS independent PHP applications. An example of how the basics of MDB work can be found under Links & Literature at the end of the article. As stated earlier, the focus of the article is to introduce the features that set MDB apart from other database abstraction layers for PHP. You can find example scripts for all code examples found in this article on the CD that is packaged with this issue.
MDB 供应了一些十分先辈的笼统特征。记住这些特征只是供选择的是很主要的。然而在编写 RDBMS 有关的 PHP 法式时利用它们长短常主要的。一个展现利用 MDB 是何等复杂的例子在文章的开头的 "链接和文献" 局部。如后面所说,文章的核心是引见使得 MDB 与其他 PHP 数据库笼统层分歧的那些特征。你可以在随本期文章一同包装的 CD 中找到一切这些例子剧本的代码。

But first we will need to get MDB installed. This is actually quite easy using the PEAR installer. I cannot cover the entire PEAR Installer within this article but I hear the next issue will talk about great details about all the ins and outs of the PEAR framework. There is work going on to make the Installer work on Windows but the support is still a bit flaky. For *nix systems you will need a CGI version of PHP installed on your system and simply run the following command:
然而,起首咱们需求把 MDB 装置上去。利用 PEAR 装置法式这其实十分轻易。我不克不及在这篇文章中完全的讲述 PEAR 装置法式然而我传闻下一期将十分具体的会商 PEAR 框架的里里外外。让装置法式运转于 Windows 的任务在停止看成然而撑持依然有一点乖僻。关于 *nix 体系你需求 PHP 的 CGI 版本装置在了你的体系而且复杂地运转上面地号令:

lynx -source go-pear.org|php

After completing the installation process you simply need to type one more command and you are all set.
在装置完成以后你只需求再输出一行号令那末就全体弄定了。

pear install MDB

If the above does not work for you there is always the option of getting the package directly from the PEAR MDB homepage. The URL is listed at the bottom of the article.
假如后面的进程对你来讲不论用,老是有从 PEAR MDB 主页中直接取得包的选项。URL 列于文章的最初。

Making use of data type abstraction
使用数据类型笼统

Since most databases tend to have some specialities or quirks it is important for MDB to hide these differences from the developer. MDB achieves this by defining its own internal data types: text, boolean, integer, decimal, float, date, time, time stamp, large objects (files). All data that is passed to and from the database may be converted from MDB's internal format to the databases internal format. The accompanying example scripts to this section can be found in the datatype directory. Let us look at the following query:
由于大局部数据库偏向于有一些特性或怪癖,关于MDB来讲把这些分歧的地方给开辟者埋没起来十分主要。MDB 经由过程界说本人的外部数据类型来到达这点:text,boolean,integer,decimal,float,date,time,time stamp,large objects(文件)。一切传递给数据库和从数据库获得的数据都能转换成 MDB 的外部格局或从数据库的外部格局转化回来。本节相干的例子剧本可以再 datatype 目次中找到。让咱们看看上面的查询:

$session = '098f6bcd4621d373cade4e832627b4f6';
// set time out to 30 minutes
$timeout = time()+60*30;
// SELECT query showing how the datatype conversion works
$query = 'SELECT createtime, user_id FROM sessions';
$query .= ' WHERE session = '.$session;
$query .= ' AND lastaccess < '.$timeout;

This query will most likely fail if it were send to a database. The reason being that the value stored in $name would need to be converted to the correct string format. This would mean the contents of $name would have to have special characters escaped and quotes placed around. PEAR DB provides the method DB:.quote() for this. In MDB the method is called MDB::getTextValue(). The difference is that MDB offers such a method for every data type listed above. So we can also convert $timeout to the correct format.
这个查询假如发送给数据库的话八成要掉败。缘由是存储在 $name 中的值需求转换为准确的字符串格局。这或许意味着 $name 的内容能够有特别的本义字符或被引号包抄。PEAR DB 为此供应了办法 DB:.quote()。在 MDB 中这个办法叫 MDB::getTextValue()。分歧的地方是 MDB 给每种后面所列的数据类型都供应了如许的函数。因此咱们也可以把 $timeout 转换为准确的格局。

// convert $timeout to the MDB timestamp format
$timeout = MDB_date::unix2Mdbstamp($timeout);
// SELECT query showing how the datatype conversion works
$query = 'SELECT createtime, user_id FROM sessions';
$query .= ' WHERE session = '.$mdb->getTextValue($session);
$query .= ' AND lastaccess < '.$mdb->getTimestampValue($timeout);

For the sake of the example let us assume that we only want to retrieve the first row. MDB::queryRow() fetches the first row, he frees the result set and returns the content, so it is exactly what we want.
为了作个演示,让咱们假定我仅仅想要获得第一行。MDB::queryRow() 取得第一行,它释放了局集而且前往其内容,因此它恰是咱们所要的。

$result = $mdb->queryRow($query);

But different RDBMS return data like dates in different formats. So, if I then want to do some date arithmetic it is important that data is always returned in the same format regardless of the RDBMS chosen. This can be done semi-automatically by MDB. All you need to do is tell MDB what type your result columns will have and MDB handles the conversion. The easiest way is to pass such information with the query method call:
然而分歧的 RDBMS 前往像日期如许的数据时用的格局是分歧的。因而,假如咱们然后要对一些数据停止盘算,不论选择的 RDBMS 是甚么,把数据以不异的格局前往是主要的。这个可以由 MDB 半主动地完成。你一切需求做的是告知你的了局列将是甚么样的类型,MDB将处置转换的任务。最复杂的举措是把如许的信息传递给查询函数。

$types = array('timestamp', 'integer');
$result = $mdb->queryRow($query, $types);

This tells MDB that the first column of the result set is of the type `timestamp' and the second is of the type `integer'. All methods that allow querying can take such meta-information as an optional parameter. The data can also be set later using MDB::setResultTypes(). Depending on the database that the data is retrieved from, it will then convert the returned data accordingly. The MDB internal data format for timestamps is the ISO 8601 standard. Other packages such as PEAR::Date can handle this format. MDB also provides a small number of methods for date format conversion in the MDB_Date class that can be included optionally.
这告知 MDB 了局集的第一列类型是 'timestamp' 和第二列是'integer'。一切查询函数可以承受如许的元信息作为可选的参数。数据还能过后用 MDB::setResultTypes() 来设置。取决于数据获得于的数据库,它然后将被响应的转换前往的数据。MDB 外部的 timestamps 的数据格局是遵守 ISO 8601 尺度的。其他像 PEAR::Date 如许的包可以处置这类格局。MDB 还在 MDB_Date 类中供应了一些数据格局转换函数,它们可以被可选的包括。

Since pretty much every RDBMS returns integer data the same way there is no need to convert integer data. So, in order to gain a slight performance improvement you could do the following:
由于相当多的 RDBMS 以不异的办法前往整数数据,没有需要转换整数数据。因此,为了取得稍许的功能改善你可以这么做:

$types = array('timestamp');
$result = $mdb->queryRow($query, $types);

This way only the first column of the result set would be converted. Of course this may become an issue if MDB would be used in conjunction with a database that does return integers differently. However unlikely, the slight performance increase might not be worth this risk. But again it shows that the usage of these features is optional.
如许只要了局集的第一列会被转换。固然,假如 MDB 用于前往整数分歧的数据库,这能够成为一个成绩。但是,稍许的功能改良能够其实不值得冒这个风险。然而再一次的,它显示了这些特征的利用仅仅是供选择的。

Listing 1 shows an example use of prepared queries. These can be quite convenient if you have to run a number of queries where the only difference is in the data that is being passed to the database while the structure of the query remains the same. Advanced databases can store the parsed query in memory to offer a performance boost.
Listing 1 展现了一个利用预筹办的查询的例子。假如你必需运转大批查询而独一的不同是数据传递给数据库,然而查询的布局仍是一样的,这些可以相当的便利。初级的数据库可以在内存中贮存解析好的查询来减速功能。

Listing 1

$alldata = array(
array(1, 'one', 'un'),
array(2, 'two', 'deux'),
array(3, 'three', 'trois'),
array(4, 'four', 'quatre')
);

$p_query = $mdb->prepareQuery('INSERT INTO numbers VALUES (?,?,?)');
$param_types = array('integer', 'text', 'text');

foreach ($alldata as $row) {
$mdb->execute($p_query, NULL, $row, $param_types);
}

Each of the 4 arrays that are stored in $alldata will be used in an execute statement. The data will automatically be converted to the correct format. Since this is an insert statement the second parameter for MDB::execute() is set to NULL because we will not have any result columns for which we would need to set data types.
在 $alldata 中贮存的一切四个数组将用于 execute 语句。数据将主动被转换为准确的格局。由于这是一个拔出语句,MDB::execute() 的第二个参数被设置为 NULL 由于咱们将没有任何了局列需求咱们设置数据类型。

Among the supported data type are also LOB's (Large OBjects) which allow you to store files into a database. Binary files are stored in BLOBs (Binary Large OBject) and normal text files are stored on CLOBs (Character Large OBject). In MDB you can only store LOB's using prepared INSERT and UPDATE queries. Using either MDB::setParamBlob() or MDB::setParamClob() you can set the values of the LOB field in a prepared query. Both methods expect to be passed a LOB object however which can be created using MDB::createLob().
在撑持的数据类型中还有 LOB (大对象),它使得咱们可以在数据库中贮存文件。二进制文件贮存在 BLOB (二进制大对象)中并且通俗文本文件贮存在 CLOB (字符大对象)中。在 MDB 中你仅仅可以利用预筹办的 INSERT 和 UPDATE 查询贮存 LOB。利用 MDBA::setParamBlob() 或 MDB::setParamClob() 你可以设置预筹办查询的 LOB 域的值。两个函数都预期传递一个 LOB 对象,而它可以利用 MDB::createLob() 创立。

$binary_lob = array(
'Type' => 'inputfile',
'FileName' => './myfile.gif'
);
$blob = $mdb->createLob($binary_lob);

$character_lob = array(
'Type' => 'data',
'Data' => 'this would be a very long string container the CLOB data'
);
$clob = $mdb->createLob($character_lob);

As you can see MDB::createLob() is passed an associative array. The value for the Type key may be one of the following: data, inputfile or outputfile. The first two are used when you want to write a LOB into the database. If you have the LOB stored in a variable you should use data while inputfile should be used to read the LOB directly from a file. Finally, outputfile should be used when you want to retrieve a LOB from the database. Depending on if you are using data or inputfile you need to specify a value for the Filename key or the Data key as seen in the above example. Now, we will store the above LOB's in the database.
如你能看到的,MDB::createLob() 被传递一个关系数组。Type 键的值多是以下中的一个:data, inputfile 或 outputfile。前两个用于你想要把 LOB 写入数据库的时分。假如你有一个贮存在变量中的 LOB,你应该在 需求利用 inputfile 时从文件直接读取 LOB。最初,outpufile 应该在你想要从数据库中读取 LOB 时利用。取决于你是不是利用数据或 inputfile 你需求给 Filename 键或 Data 键指定一个值,像下面的例子那样。如今,咱们将把后面的 LOB 贮存到数据库中去。

$p_query = $mdb->prepareQuery('INSERT INTO files (id, b_data, c_data) VALUES (1, ?, ?)');

$mdb->setParamBlob($p_query, 1 , $blob, 'b_data');
$mdb->setParamClob($p_query, 2 , $clob, 'c_data');

$result = $mdb->executeQuery($p_query);

In order to fetch the above file from the database we will need to first select the data from the database and create a LOB object using MDB::createLob(). This time we will set `Type' to `outputfile'.
为了从数据库中获得下面的文件,咱们需求起首从数据库当选择数据而且利用 MDB::createLob() 创立 LOB 对象。此次咱们将设置 'Type' 为 'outputfile'

$mdb->query('SELECT b_data FROM files WHERE id = 1');

$binary_lob = array(
'Type' => 'outputfile',
'Result' => $result,
'Row' => 0,
'Field' => 'b_data',
'Binary' => 1,
'FileName' => './myfile2.gif'
);
$blob = $mdb->createLob($binary_lob);

Now we can read the LOB from the result set using MDB::readLob(). Passing a length of 0 to MDB::readLob() means that the entire LOB is read and stored in the file we specified above. Once we are done we can free the resources. Alternatively, you can set any length larger than zero and read the LOB using a while loop checking MDB::endofLob().
如今咱们可以利用 MDB::readLob() 从了局集中读取 LOB。传递长度 0 给 MDB::readLob() 意味着全部 LOB 被读取和贮存在咱们后面指定的文件中。一旦义务完成了,咱们可以把资本释放了。你也能够设置任何大于零的长度而且利用一个 while 轮回反省 MDB::endofLob() 来读取 LOB。

$mdb->readLob($blob, $data, 0);

It is important to note that you may not mix this method of fetching with the bulk fetching methods like MDB::fetchAll() as this will cause problems in most PHP database extensions. At some point MDB may be able to retrieve LOB's using the bulk fetching methods.
注重你不要把这个获得函数和 bulk 获得函数像 MDB::fetchAll()弄混了,由于这将在大局部 PHP 数据库扩大中招致成绩。在一些时分,MDB 能够可以利用 bulk 获得函数取得 LOB。

As we have seen in this section MDB features its own set of native data types that are automatically mapped to native data types in the database. This ensures that no matter what data we send or retrieve from the database it will always be in the same format no matter what RDBMS is used. As I have mentioned in the opening paragraph of this section this obviously requires that the data types used in the database are what MDB expects. This requirement was made to ensure that the mapping is done with a minimal performance loss. The next section will teach us how MDB assists with using the correct data types in the database.
如咱们在这节所见,MDB 特征自己的原生数据类型集主动映照于数据库中的原生数据类型。这包管了不管咱们发送和从数据库吸收甚么样的数据,它都能与利用的 RDBMS 有关的利用不异的格局。如我在本节开篇已提到的,这分明需求数据库利用的数据类型是 MDB 预期的。这类需求被用于确保映照所消耗的价值很小。下一节将教给咱们 MDB 若何帮助在数据库中利用准确的数据类型。

Making use of XML schema files
利用 XML schema 文件

With the features described in the last paragraph you can write truly database independent applications. But MDB tries to go one step further: It allows you to define your schemas in XML. A manager converts this schema into the necessary SQL statements for each RDBMS. This means that you can use the same schema for any of the supported RDBMS. The examples for this section can be found in the xml_schema directory.
使用在上个段落中描写的特征,你能编写真实的数据库自力的法式。然而 MDB 测验考试向前加倍迈出一步:它答应你用 XML 界说你的 schema。一个办理器把这类 schema 转换为给每种 RDBMS 的需要的 SQL 语句。这意味着你能对一切撑持的 RDBMS 利用不异的 schema。本节的例子可以在 xml_schema 目次中找到。

We will now write an XML schema file from scratch. First we must define an XML document. The database definition is contained within a database tag. The name of the database is defined using the name tag. The create tag tells the manager if the database should be created if it does not yet exist. If you split up your schema into several files you will only need to set create to 1 in the file you will submit first to the manager.
咱们如今将从头编写一个 XML schema 文件。起首,咱们必需界说一个 XML 文档。数据库界说是包括在一个 database 标签当中的。数据库的名字是利用 name 标签界说的。create 标签告知办理器数据库是不是需求在它不存在的时分被创立。假如你把你的 schema 文件朋分成好几个文件你你起首提交给办理器的谁人文件中把 create 设置为 1。

<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name>auth</name>
<create>1</create>
</database>

As you may have guessed from the database name auth the purpose of this database is to store user data for a simple authentication application. Listing 2 defines a table in which we can store the user data.
能够你已从数据库名 auth 猜出了这个数据库的目标是用于贮存复杂的验证法式的用户数据。Listing 2 界说了在个中咱们能贮存用户数据的表。

Listing 2

<table>
<name>users</name>
<declaration>
<field>
<name>user_id</name>
<type>integer</type>
<notnull>1</notnull>
<unsigned>1</unsigned>
<default>0</default>
</field>
<field>
<name>handle</name>
<type>text</type>
<length>20</length>
<notnull>1</notnull>
<default></default>
</field>
<field>
<name>is_active</name>
<type>boolean</type>
<notnull>1</notnull>
<default>N</default>
</field>
</declaration>
</table>


As you can see, things can get a bit lengthy here as to be expected when using XML. No need to worry: We are working on a browser based tool called MDB_frontend that will make this process much easier. I will talk about this project further down into this article a bit more. Hopefully, the advantage of this pretty verbose representation of the table is that things are somewhat self explanatory. The table in the last example is called users and we have defined 3 fields: user_id of type integer, handle of type text and is_active of type boolean. Remember that MDB handles the type abstraction for you if you pass it the necessary metadata as shown in the previous section. You also need not to worry about what MDB maps these types to in your RDBMS. The other tags you can use in each of the field declarations are optional: length, notnull, unsigned and default.
如你能看到的,如利用 XML 时可以预期的,器材变得有一些冗杂。不必忧虑:咱们有一个基于阅读器的东西称为 MDB_frontend 使得这个进程加倍复杂。我将在这篇文章的前面议论这个工程。能够这极为具体地表格描写的长处长短常分明。后面例子中的表格被称为 users 而且咱们界说了 3 个域:类型为整数的 user_id,类型为文本的 handle 和类型为逻辑型的 is_active。记住假如你如前一节那样传递了需要的元数据 MDB 为你处置类型笼统。你还不需求 MDB 把这些类型映照为你的 RDBMS 中的甚么。在每一个域声明中还能利用的其他标签是可选的:length,notnull,unsigned 和 default。

The next thing that we now need to do is to ensure that the user_id is unique by placing the proper index on the user_id field. The index definition goes within the declaration tag (Listing 3).
下一件咱们如今需求做的工作是经由过程在 user_id 域放置得当的索引确保 user_id 是独一的。索引界说就在声明标签以内(Listing 3)。

Listing 3:

<table>
<name>users</name>
<declaration>
<index>
<unique>1</unique>
<name>user_id_index</name>
<field>
<name>user_id</name>
<sorting>ascending</sorting>
</field>
</index>
</declaration>
</table>

The definition in listing 3 would create a unique ascending index named user_id_index on the field user_id. Of course, we could have specified more than one field in the index definition by simply adding another field tag. What we are still missing now is a sequence to generate unique user id's for us:
在 listing 3 中的界说在域 user_id 中创立一个独一的上升排序的名为 user_id_index 的索引。固然,咱们可以复杂地添加别的一个域标签在索引界说中指定多于一个的域。咱们如今依然没有提到的是为咱们发生独一的用户 id 的序列。

<sequence>
<name>users_user_id</name>
<start>1</start>
<on>
<table>users</table>
<field>user_id</field>
</on>
</sequence>

The last example is pretty mind blowing. Going through line by line we see that we first open a sequence tag followed by a name tag which specifies the name of the sequence. This is followed by a start tag that defines the initial value of the sequence. Now, we open an optional on tag. Here we need to set a specific field within a table. This information is used by the manager to set the value of the sequence to the maximum value in the user_id field of the users table. If the users table is empty the value specified in the start tag is used instead. Please note that the value specified in the start tag is the first value that will be returned if you call MDB::nextId().
上一个例子十分的绕弯。一行行看过去,咱们看到起首翻开一个 sequence 标签,随着一个指定序列名字的 name 标签。这以后随着一个界说序列初始值的 start 标签。如今,咱们翻开一个可选的 on标签。这儿咱们需求设置一个表中的指定域。这个信息是办理器用来把序列的值设置为 users 表的 user_id 域的最大值。假如 users 表是空的,作为替换利用的是 start 标签中指定的值。请注重在 start 标签中指定的值是咱们挪用 MDB::nextId() 前往的第一个值。

Of course, you can also initialize a table with any values. For example you may want to initialize the above table with a maintenance user that you always want to include with your application. To do this we need to add an initialization tag to the table tag. Listing 4 defines one row after another enclosed with an insert tag.
固然,你也能利用任何值初始化表。例如你能够想要用你老是想要包括在你的法式中的办理用户来初始化后面的表格。为了这么做,咱们需求把一个 initialization 标签添加给 table 标签。Listing 4 界说了一在别的一用 insert 标签包含的行以后的行。

Listing 4

<table>
<name>users</name>
<initialization>
<insert>
<field>
<name>user_id</name>
<value>1</value>
</field>
<field>
<name>handle</name>
<value>default</value>
</field>
<field>
<name>is_active</name>
<value>Y</value>
</field>
</insert>
</initialization>
</table>

As you can see from the last example all we have to do is to define a value for each field of the table. We now have the necessary basics to create an XML schema for MDB. The next step is to pass this schema file to the MDB manager.
如你从上个例子中能看到的那样,一切咱们需求做的就是给表的每一个域设定值。咱们如今已晓得了需要的基本常识来创立一个 MDB 的 XML schema。下一步是把这个 schema 文件传递给 MDB 办理器。

$manager = new MDB_Manager;
$input_file = 'auth.schema';
// we do not have to connect to a specify a specific database at this time
$dsn = "mysql://$user:$pass@$host";
$manager->connect($dsn);
$manager->updateDatabase($input_file, $input_file. '.before');

We now have a new database called auth with a table called users. There is one index on the field user_id. There is one row in the table as well. We also have a sequence called users_user_id which will be initialized at 1. The next value in the sequence will therefore be 2. Finally, a copy of the schema was created with the name auth.schema.before. This happened because we passed the optional second parameter to MDB_Manager::updateDatabase(). In the next section we will see why this copy is created.
咱们如今有了一个新的名字叫 auth 的数据库,它有一个表叫 users。在域 user_id 有一个索引。并且在表中还有一行。咱们还有一个序列称为 users_user_id,它将被初始化为 1。因而序列中的下一个值就是 2。最初,schema 的一个拷贝以名字auth.schema.before 被创立。这是由于咱们给 MDB_Manger::updateDatabase() 传递了可选的第二个参数。鄙人一节咱们将看到为何要创立这个拷贝。

This is all fairly amazing but it gets better. It is often the case an application needs to be changed at some point. For example we may decide we want to change the name of the table from users to people. We also want to add a field called pwd to store the password field (please check the textbox Reserved Words).
一切这些都十分使人惊异然而它变得更好。很多情形下法式需求在某些中央作出改动。例如咱们能够决意需求把表的名字从 users 酿成 people。咱们能够还需求增添一个域 pwd 来贮存暗码域(请反省 textbox 的保存字)。

Reserved Words
保存字

The reason we do not call the field password is that this is a reserved word for field names in Interbase. Since we want to be RDBMS independent the MDB manager will either issue a warning or fail if the option fail_on_invalid_names is set to true (which is the default).
咱们没有称谁人域为 password 的缘由是那是 Interbase 中一个域名的保存字。由于咱们需求 RDBMS 自力,MDB 办理器要末给出一个正告要末在 fail_on_invalid_names 选项被设置为真的时分(这是缺省值)掉败。

In the old days you would now be in a bit of pain to alter all your existing installations to this new schema. But thanks to MDB this can be automated. In listing 5 are the changes we make to our table definition:
在曩昔的时分,你能够如今正处于把你一切已有的器材酿成这类新的 schema 的疾苦当中。然而因为 MDB 这些任务可以主动完成。在 listing 5 中是咱们对咱们的表格界说停止的修正:

Listing 5

<table>
<name>people</name>
<was>users</was>
<declaration>
<field>
<name>pwd</name>
<type>text</type>
<length>32</length>
<notnull>1</notnull>
<default></default>
</field>
</declaration>
</table>

Now we want the manager to make the necessary alterations, but before I want to mention a possible pitfall. Since we renamed the table users to people we also have to change all references to the old name like in the sequence we build. There the reference in the on tag needs to be changed to point to the people table. To achieve this we pass the new and the old version of the schema to the manager. This is why we created a .before file when we first called MDB_Manager::updateDatabase(). This ensures that we have an old version of the schema to compare the new version with.
如今咱们想要办理器来作出需要的改动,然而在此之前我像提一下能够的圈套。由于咱们把表从 users 改名为 people,咱们还需求把一切对本来名字的援用停止更改,好比咱们创立的序列。在 on 标签中的索引需求更改成指向 people 表。为了到达这个目标,咱们把 shcema 的新旧版本传递给办理器。这酒是为何咱们在第一次挪用 MDB_Manager::updateDatabase() 时咱们创立一个 .before 文件的缘由。这确保了咱们有一个旧版本的 shcema 来与新的版本停止对比。

$input_file = 'auth.schema';
$manager->updateDatabase($input_file, $input_file.'.before');

That's all! The users table is now called people and now we also have a pwd field.
一切的就是如许!users 体现在称为 people 而且咱们也有了一个 pwd 域。

I now want to look at one last feature of the XML schema format. This feature is especially important if you want to programmatically use the manager. Imagine that you have several customers that have the same authentication application running on your database server. Every customer has a database running on this server with the same schema but one minor difference: the name of the database. While it may be feasible to keep separate schema files for each client because the update cycles will not be the same this is not the case for our sample authentication application. Here all clients will be updated at the same time. The XML schema format allows us to use the variable tag for this.
我如今要看看 XML schema 格局的最初一个特征。假如你想要编程性的利用办理器,这个特征特别主要。假定你有好几个有不异验证法式运转在你的数据库办事器的客户。 每一个客户有一个办事器运转在这个办事器有不异的 schema 只要巨大的区分:数据库的名字。能够为每一个客户独自保留 schema 文件是可行的由于更新周期能够不是一样的,这不是咱们例子验证法式的情形。这儿一切的客户同时更新。XML schema 文件答应咱们为此可使用变量。

<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name><variable>name</variable></name>
</database>

We can now set the variable name at run time to whatever we may need.
咱们如今在运转时设置变量为恣意咱们需求的器材。

foreach($clients as $name) {
$variables = array('name' => $name)
$manager->updateDatabase($input_file, $input_file.'.before', $variables);
}

The XML schema management is another important piece in the database abstraction concept that MDB provides. It allows us to keep our schema definition independent of a specific RDBMS. But using this format also ensures that the correct native data types are used so that MDB can correctly map its native data types. Finally, since the format is based on XML it is much easier to write tools that generate or read XML schema files.
XML schema 办理是 MDB 供应的数据库笼统概念的别的一个十分主要的局部。它使得咱们坚持咱们的 schema 界说与特定的 RDBMS 有关。然而利用这个格局还确保了利用准确的原生数据类型因此 MDB 可以准确地映照它的原生数据类型。最初,由于数据是基于 XML 的,编写发生或读取 XML schema 文件的东西要轻易一些。

Sounds great but my application already uses ...
听起来不错然而我的使用法式已利用了……

Most readers probably find themselves in a position where they already have a number of applications that run on some other database abstraction layer. Due to MDB's heritage most PEAR DB users should find that MDB feels very similar, since the API of MDB is based on that of PEAR DB. Metabase users should find that all their favourite functions have their counterpart in MDB. The XML schema format is exactly the same as in Metabase. A complete guide to porting your existing applications to MDB is beyond the scope of this article, instead I will use this space to give some tips. If you have any specific questions feel free to email me.
大局部读者能够发明它们处于如许的地步??他们已有了大批运转于其他数据库笼统层的法式。因为 MDB 的出生,大局部 PEAR DB 的用户应该发明 MDB 感到上十分相似,由于 MDB 的 API 是基于 PEAR DB 的。Metabase 用户应该发明他们一切偏心的功效都在 MDB 中有对应的器材。XML schema 格局和 Metabase 中的是一摸一样的。一个完整的指点来引诱你把已写好的法式移植到 MDB 中超越了本文的局限,然而我将使用这个时机给一些提醒。假如你有任何详细的成绩,宁神的发信来扣问我。

To port your PEAR DB application to MDB the best place to start is the PEAR wrapper. For one you can run your application using the PEAR wrapper. The wrapper of course does add a little bit of overhead so you will probably want to port to the native interface at some point. The first step then should be listing all PEAR DB methods that your application currently uses. Then look at the wrapper for any differences in the API. There are two key differences you will notice: result sets are not objects anymore and all of the querying methods allow you to pass the data types of the result set which will result in slight changes in the parameter order. The first difference means that instead of calling the fetch method on the result object:
为了把你的 PEAR DB 法式移植到 MDB,最好的出发点是 PEAR wrapper。你能利用 PEAR wrapper 来运转你的法式。wrapper 固然增添了一些额定承当,因此你能够有些想要移植到原生的接口。那末第一步是列出一切你法式以后利用的 PEAR DB 函数。然后看看 wrapper 从中找出任何 API 上的区分。有两个你要注重的关头区分:了局集不再是对象并且一切的答应你传递了局集的数据类型的查询办法将招致参数按次上的少量改动。第一个区分意味着不克不及再了局对象上挪用获得函数。

$result = $db->query($sql);
$row = $result->fetchRow();

You will now have to call the MDB object for fetching:
你如今必需挪用 MDB 对象来停止获得:

$result = $mdb->query($sql);
$row = $mdb->fetchRow($result);

The second difference is quite easily fixed by looking at the wrapper. As you can see in the wrapper you may simply pass NULL where MDB would otherwise expect data types in the result set. Now, your application should work with MDB. Of course, you are now not really taking advantage of the advanced features of MDB. This most likely will require some changes to your current database schema. The manager can attempt to reverse engineer an XML schema file from an existing database. A very simple front end can be found in the MDB package: the reverse_engineer_xml_schema.php script. Most likely you will need to manually fix the resulting XML schema file, but it will give you a nice starting point.
第二个区分经由过程察看 wrapper 可以等闲的被处理。如你再 wrapper 中能看到的,你可以再 MDB 希冀失掉了局集的数据类型的中央复杂地传递 NULL。如今,你地法式应该可以利用 MDB。固然,你如今没有真正失掉了 MDB 地初级特征长处的好处。这最有能够的是需求对你以后的数据库 schema 停止一些修改。办理器可以测验考试反向地从已存在的数据库中获得 XML schema 文件。一个十分复杂的前端可以在 MDB 包中找到:reverse_engineer_xml_schema.php 剧本。极有能够你将需求手动修改发生的 XML schema 恩见,然而它将给你一个很好的入手下手。

If you want to port your existing application from Metabase to MDB you will have to change all of your function calls. Looking at the Metabase wrapper it will become quite obvious what changes need to be made. If you know regular expressions well you might even be able to get most of the work done with a few such replacements. Anyways, you should be up and running your old beloved advanced abstraction features but now using MDB in no time. What you will probably notice is that the method names are much shorter now. If you do some benchmarking you will also see a nice performance increase.
假如你想要把你已存在的法式从 Metabase 移植到 MDB 你将必需修改一切的函数挪用。检查 Metabase wrapper 需求修改甚么将变得十分分明。假如你晓得正则表达式你能够可以完成大局部如许的交换任务。不管若何,你应该向前而且运转你本来喜欢的初级笼统特征然而如今用的是 MDB。你能够注重失掉的是函数名变得加倍冗长了。假如你作一些功能测试,你也将看到可不雅的功能改良。

So what does the future look like for MDB?
那末 MDB 未来会是甚么模样呢?

At the time this article publishes MDB will have moved on from the original 1.0 release. Next to the original MySQL and PostGreSQL drivers MDB will also have an ODBC driver and possibly even more drivers. This is one key area that is focused on during the development of MDB. Once MDB has caught up with PEAR DB in terms of drivers it is likely to become the standard database abstraction layer in the PEAR framework.
本文宣布时 MDB 能够已不再是本来的 1.0 release 了。在本来的 MySQL 和 PostGreSQL 驱动以后,MDB还将有一个 ODBC 驱动和能够的更多的驱动。这是 MDB 开辟过程当中存眷的关头区域之一。一旦 MDB 在驱动方面跟上了 PEAR DB,它很有能够成为 PEAR 框架中尺度的数据库笼统层。

But there is another key area of development: the MDB_frontend project. The MDB_frontend will be a phpMyAdmin like webfrontend based on MDB and the MDB manager. With this tool you will be able to browse databases stored on any RDBMS that MDB supports. The MDB_frontend will show both the native and the MDB data types. Emulated features such as sequences in MySQL will be hidden. The user will simply see a list of sequences and not a table storing the value of the sequence which is how sequences are emulated in MySQL. Furthermore the MDB_frontend will assist in porting existing databases to match the native data types that MDB expects to be used. It will also help in creating and updating XML schema files. Some initial work has been completed but much more work is needed before a public release can be expected.
然而还有别的一个开辟中的关头范畴:MDB_frontend 工程。MDB_frontend将成为基于 MDB 和 MDB 办理器的 phpMyadmin。有了这个东西,你将可以阅读贮存在 MDB 撑持的 RDBMS 中的数据库。MDB_frontend 将同时显示原生和 MDB 数据类型。摹拟的特征好比 MySQL 中的序列将被埋没。用户将仅仅看到一个序列列表而不是一个贮存序列指的表,而在 MySQL 中这就是序列是若何被摹拟的。并且 MDB_frontend 将匡助移植已存在的数据库来合适 MDB 预期利用的原生数据类型。它还将匡助创立和更新 XML schema 文件。一些早期的任务已完成了然而良多任务需求在公然宣布之前被添加。

While drivers and the MDB_frontend are the focus of all development currently, there are other things that MDB users may need: Like the integration of bulk fetching of LOB fields, others may need foreign and primary key support. As always in opensource things will go faster if you participate in testing and implementation. But I am also thankful for any other feedback like feature requests.
驱动和 MDB_frontend 是以后开辟的一切核心,在 MDB 中还有很多用户能够需求的:像 bulk 获得 LOB 域的集成,其别人能够需求内部和主键撑持。如一向以来的那样假如你介入测试和完成,开源的器材将加速良多。然而我也很感激像特征需求合阳的反应。

Some final thoughts
一些文后的思虑

After months of hard work MDB is gaining acceptance among the current PEAR DB and Metabase users. I also hope that people that so far have not been convinced by other database abstraction layers realize the benefits that MDB holds for them. Of course, there are still a lot of applications that need to be tailored specifically to one RDBMS where a tool like MDB just ads unnecessary overhead and restrictions. Overall I am very pleased that we made the decision in my company to lead the MDB development. In the beginning, we were all a bit worried that by attempting to please both the PEAR DB and Metabase users the result would end up pleasing neither side. Another source of concern was if the PHP community would assist in the development or not. I am very happy that the PHP community came through and helped in writing drivers and helping on the core of MDB as well. Therefore we consider this project to be a huge success. We are sure that together MDB will be improved even further. And we are happy to have helped making PHP even better.
在数月的艰苦任务以后,MDB 正在以后的 PEAR DB 和 Metabase 用户中取得承认。我还但愿以后还没有被其他数据库笼统层压服的用户意想到 MDB 给他们的优点。固然,仍是有很多法式需求对 RDBMS 停止特别剪裁,关于这类情形像 MDB 如许的东西仅仅是增添了不用要的额定承当和限制。总的来讲,我十分乐意咱们在咱们的公司中作出向导 MDB 开辟的决意。在后来,我对测验考试同时取悦 PEAR DB 和 Metabase 的用户然而了局能够各处不奉迎几何有些忧虑。别的一个关怀的来历是 PHP 社区是不是将匡助其开辟。我十分乐意 PHP 社区来了而且匡助撰写驱动和 MDB 的中心。因此咱们以为这个项目是一个极大的胜利。咱们还一并信任 MDB 将失掉更大的改善。并且咱们对匡助 PHP 变得更好感应乐意。

Lukas Smith is the lead author of PEAR MDB. He actively contributes to various PHP opensource projects and is a founder of the company BackendMedia which specializes in PHP development.
Lukas Smith 是 PEAR DB 的次要作者。它积极地给多个 PHP 开远项目停止奉献而且是专注于 PHP 开辟的 BackendMeida 公司的创立者。

Links and Literature
链接 和 文献

PEAR MDB homepage: pear.php.net/package-info.php?package=MDB

PEAR MDB documentation: www.backendmedia.com/MDB/docs/

PEAR MDB sample script: cvs.php.net/co.php/pear/MDB/MDB_test.php

PEAR DB homepage: pear.php.net/package-info.php?package=DB

Metabase homepage: www.phpclasses.org/mirrors.html?page=%2Fbrowse.html%2Fpackage%2F20.html

Simple benchmark: freshmeat.net/screenshots/30313/


  既然选择了PHP,就要坚持学下去!大家有没有问自己为什么会选择学习PHP呢?就我个人而言,完全是因为兴趣,因为我的专业和计算机完全无关,但是就是对编程很赶兴趣,尤其对网络编程、web开发特别赶兴趣。
若相依 该用户已被删除
沙发
发表于 2015-2-16 04:55:31 | 只看该作者
实践是检验自己会不会的真理。
海妖 该用户已被删除
板凳
发表于 2015-2-16 04:55:31 | 只看该作者
使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。
小魔女 该用户已被删除
地板
发表于 2015-3-11 18:24:42 | 只看该作者
当然这种网站的会员费就几十块钱。
柔情似水 该用户已被删除
5#
 楼主| 发表于 2015-3-11 22:50:04 | 只看该作者
首推的搜索引擎当然是Google大神,其次我比较喜欢 百度知道。不过搜出来的结果往往都是 大家copy来copy去的,运气的的概率很大。
不帅 该用户已被删除
6#
发表于 2015-3-13 00:09:38 | 只看该作者
当然这种网站的会员费就几十块钱。
分手快乐 该用户已被删除
7#
发表于 2015-3-15 22:04:53 | 只看该作者
个人呢觉得,配wamp 最容易漏的一步就是忘了把$PHP$目录下的libmysql.dll拷贝到windows系统目录的system32目录下,还有重启apache。
小女巫 该用户已被删除
8#
发表于 2015-3-17 00:09:04 | 只看该作者
刚开始安装php的时候,我图了个省事,把php的扩展全都打开啦(就是把php.ini 那一片 extension 前面的冒号全去掉啦),这样自然有好处,以后不用再需要什么功能再来打开。
愤怒的大鸟 该用户已被删除
9#
发表于 2015-3-19 14:47:40 | 只看该作者
在我安装pear包的时候老是提示,缺少某某文件,才发现 那群extension 的排列是应该有一点的顺序,而我安装的版本的排序不是正常的排序。没办法我只好把那群冒号加了上去,只留下我需要使用的扩展。
飘灵儿 该用户已被删除
10#
发表于 2015-3-20 12:25:15 | 只看该作者
建议加几个专业的phper的群,当然啦需要说话的人多,一处一点问题能有人回答你的,当然啦要让人回答你的问题,平时就得躲在里面聊天,大家混熟啦,愿意回答你问题的人自然就多啦。
变相怪杰 该用户已被删除
11#
发表于 2015-3-31 14:23:35 | 只看该作者
首推的搜索引擎当然是Google大神,其次我比较喜欢 百度知道。不过搜出来的结果往往都是 大家copy来copy去的,运气的的概率很大。
精灵巫婆 该用户已被删除
12#
发表于 2015-4-10 17:02:51 | 只看该作者
,熟悉html,能用div+css,还有javascript,优先考虑linux。我在开始学习的时候,就想把这些知识一起学习,我天真的认为同时学习能够互相呼应,因为知识是相通的。
透明 该用户已被删除
13#
发表于 2015-4-21 06:39:29 | 只看该作者
,熟悉html,能用div+css,还有javascript,优先考虑linux。我在开始学习的时候,就想把这些知识一起学习,我天真的认为同时学习能够互相呼应,因为知识是相通的。
灵魂腐蚀 该用户已被删除
14#
发表于 2015-4-23 07:07:41 | 只看该作者
找到的的资料很多都是在论坛里的,需要注册,所以我一般没到一个论坛都注册一个id,所有的id都注册成一样的,这样下次再进来的时候就不用重复注册啦。当然有些论坛的某些资料是需要的付费的。
山那边是海 该用户已被删除
15#
发表于 2015-4-25 07:49:55 | 只看该作者
微软最近出的新字体“微软雅黑”,虽然是挺漂亮的,不过firefox  支持的不是很好,所以能少用还是少用的好。
兰色精灵 该用户已被删除
16#
发表于 2015-4-26 19:11:00 | 只看该作者
最后介绍一个代码出错,但是老找不到错误方法,就是 go to wc (囧),出去换换气没准回来就找到错误啦。
乐观 该用户已被删除
17#
发表于 2015-5-10 22:02:56 | 只看该作者
遇到出错的时候,我经常把错误信息直接复制到 google的搜索栏,一般情况都是能搜到结果的,不过有时候会搜出来一大片英文的出来,这时候就得过滤一下,吧中文的弄出来,挨着式方法。
只想知道 该用户已被删除
18#
发表于 2015-5-11 04:43:35 | 只看该作者
最后祝愿,php会给你带来快乐的同时 你也会给他带来快乐。
小妖女 该用户已被删除
19#
发表于 2015-6-8 21:12:27 | 只看该作者
开发工具也会慢慢的更专业,每个公司的可能不一样,但是zend studio是个大伙都会用的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-4-30 23:24

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表