博客列表 >PDO和PDOStatement的预习笔记

PDO和PDOStatement的预习笔记

Dseven
Dseven原创
2020年02月10日 00:31:111573浏览

完成寒假作业时看着PDO有点头大,为了在老师上课时能跟上进度,我将PHP.net手册关于PDO和PDOStatement的入门内容进行了简单的集合,方便学习时查看和记忆。

1.是什么?

PDO是一个类,它代表PHP和数据库服务之间的一个连接。
PDOStatement也是一个类,代表一条预处理语句,并在该语句被执行后代表一个相关的结果集。

2.POD类的常用方法

2.1 PDO::__construct()

作用:创建一个表示数据库连接的 PDO 实例
参数
string $dsn:必须参数。一个 DSN 由 PDO 驱动名、紧随其后的冒号、以及具体 PDO 驱动的连接语法组成。例如:$dsn = ’mysql:host=localhost;dbname=databasename‘;
string $username:可选参数。DSN字符串中的用户名,也就是数据库的用户名。
string $password:可选参数。DSN字符串中的密码,也就是链接数据库的密码。
array driver_options:可选参数。一个具体驱动的连接选项的键=>值数组。
返回值:如果成功则返回一个PDO对象。
用法示例$pdo = new PDO('mysql:host=localhost;dbname=*','username','password')

2.2 PDO::prepare()

作用:准备要执行的语句
参数
string $statement:必须参数。 对目标数据库服务器有效的 SQL 语句模板,也就是一条计划执行的SQL语句。
array drive_options:可选参数。数组包含一个或多个 key=>value 键值对,为返回的 PDOStatement 对象设置属性。 常见用法是:设置 PDO::ATTR_CURSOR 为 PDO::CURSOR_SCROLL,将得到可滚动的光标。
返回值:返回语句对象,即PDOStatement类的一个实例化对象。
用法示例
方法1$sql = $pdo->prepare('select 字段1 字段2 from 表名 )
方法2$sql = $pdo->prepare('select 字段1 字段2 from 表名 where 查询条件1:占位字符 and 查询条件2 :占位字符')
SQL语句可以包含零个或多个参数占位标记,格式是命名(:name)或问号(?)的形式,当它执行时将用真实数据取代。
在同一个 SQL 语句里,命名形式和问号形式不能同时使用;只能选择其中一种参数形式。 请用参数形式绑定用户输入的数据,不要直接字符串拼接到查询里。

2.3 PDO::query()

作用: 执行 SQL 语句。
参数
statement:必须参数。需要准备、执行的 SQL 语句。注意刚入门,我们只用这一个参数。
返回值:以 PDOStatement 对象形式返回结果集,注意返回的是一个对象,而不是一个数组,但是可以像数组那样用foreach来遍历其中的内容。
用法示例
$pdo->query(’select 字段 from 表名‘);
注意 在单次函数调用内执行 SQL 语句,以 PDOStatement 对象形式返回结果集(如果有数据的话)。
如果反复调用同一个查询,用 PDO::prepare() 准备 PDOStatement 对象,并用 PDOStatement::execute() 执行语句,将具有更好的性能。
对于在程序中只需要发出一次的 SELECT 语句,可以考虑使用 PDO::query()。
对于需要发出多次的语句,可用 PDO::prepare() 来准备一个 PDOStatement 对象并用 PDOStatement::execute() 发出语句。
我理解的意思是,只用一次就选这条语句,如果需要反复查询,就用上面的perpaer()设置之后用PDOStatement中的execute()执行。

2.4 PDO::quote()

作用: 为 SQL 查询里的字符串添加引号。这样做可以更加安全。
参数
string $string:必须参数。要添加引号的字符串。
int parameter_type:可选参数。为驱动提示数据类型,以便选择引号风格。这里应该使用和函数配合的预定义常量,例如:PDO::PARAM_STR,这有点像过滤器。
返回值:经过处理的字符串。
注意
如果使用此函数构建 SQL 语句,强烈建议使用 PDO::prepare()配合参数构建,而不是用

2.5 PDO::quote()

作用:把用户输入的数据拼接进 SQL 语句。
使用 prepare 语句处理参数,不仅仅可移植性更好,而且更方便、免疫 SQL 注入;相对于拼接 SQL 更快,客户端和服务器都能缓存编译后的 SQL 查询。
手册中笔记显示:用过滤器或者字符串替换函数可能效率更好。

2.6 PDO::exec()

作用:执行一条 SQL 语句
参数
string $statement:必须参数。需要执行的SQL语句。
注意这个方法是不会从一条 SELECT 语句中返回结果的。
返回值:返回受影响的行数

2.7 PDO::lastInsertId()

作用: 返回最后插入行的ID或序列值
参数
string name:可选参数,如不选默认值为NULL。应该返回ID的那个序列对象的名称。
返回值
如果没有为参数 name 指定序列名称,PDO::lastInsertId() 则返回一个表示最后插入数据库那一行的行ID的字符串。
如果为参数 name 指定了序列名称,PDO::lastInsertId() 则返回一个表示从指定序列对象取回最后的值的字符串。

2.8 其他方法

以下方法下次再来细说
事务有关的方法
PDO::beginTransaction — 启动一个事务
PDO::commit — 提交一个事务
PDO::rollBack — 回滚一个事务
PDO::inTransaction — 检查是否在一个事务内
查询设置数据库连接属性的方法
PDO::getAttribute — 取回一个数据库连接的属性
PDO::setAttribute — 设置属性
PDO::getAvailableDrivers — 返回一个可用驱动的数组
错误提示的方法
PDO::errorCode — 获取跟数据库句柄上一次操作相关的 SQLSTATE
PDO::errorInfo — Fetch extended error information associated with the last operation on the database handle

3.PDOStatement类的常用方法

注意PDOStatement一般不需要显示的进行实例化,因为在PDO的方法中已经完成了这一步。

3.1 PDOStatement::execute()

作用:执行一条预处理语句
参数
array input_parameters:一个元素个数和将被执行的SQL语句中绑定的参数一样多的数组,所有的值作为 PDO::PARAM_STR 对待。
注意是否用这个参数,要看perpare()中的SQL语句中是否使用了参数标记,如果采用了参数标记,并且你不打算用参数绑定的话,就需要填入这个参数。
不能绑定多个值到一个单独的参数;比如,不能绑定两个值到 IN()子句中一个单独的命名参数。
绑定的值不能超过指定的个数:如果在 input_parameters 中存在比 PDO::prepare() 预处理的SQL 指定的多的键名,则此语句将会失败并发出一个错误。
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。
用法示例
注意这里只演示了,需要在函数中使用参数的情况,如果想用参数绑定的方式达到同样的目的,请看PDOStatement::bindValue()中的例子。

    /* 通过传递一个含有插入值的数组执行一条预处理语句 */    $calories = 150;    $colour = 'red';    //这里是假设查询fruit表中的name,colour,calories三个字段,查询条件是calories小于150,并且颜色是红色    //**注意**这条SQL语句中使用了参数标记,也就是占位符,:calories和:colour    $sth = $pdo->prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour');    $sth->execute(array(':calories' => $calories, ':colour' => $colour));

3.2 PDOStatement::bindColumn()

作用: 绑定一列到一个 PHP 变量,也就是安排一个特定的变量绑定到一个查询结果集中给定的列。
每次调用 PDOStatement::fetch()PDOStatement::fetchAll()都将更新所有绑定到列的变量。
注意在语句执行前 PDO 有关列的信息并非总是可用,一般我们在 PDOStatement::execute()执行之后 调用此函数
参数
mixed column:必须参数。结果集中的列号(从1开始索引)或列名。如果使用列名,注意名称应该与由驱动返回的列名大小写保持一致。
mixed param:必须参数。将绑定到列的 PHP 变量名称
int type:可选参数。通过 PDO::PARAM_ 常量指定的参数的数据类型。
int maxlen:可选参数。预分配提示。
mixed driverdata:驱动的可选参数(我理解文中说的驱动,就是你选择的那个数据库类型,因为PDO就是针对不同数据库提供不同类型的驱动)。
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。
*
用法示例:
```php
   function readData($pdo) {
       $sql = ‘SELECT name, colour, calories FROM fruit’;
       try {
                $stmt = $pdo->prepare($sql);//PDO类的SQL语句预处理函数,返回了一个PDOStatement对象
                $stmt->execute();//PDOStatement类的执行语句,返回了一个对象结果集

             /*  通过列号绑定  */             $stmt->bindColumn(1, $name);//绑定方法1,列号是从1开始的             $stmt->bindColumn(2, $colour);             /*  通过列名绑定  */             $stmt->bindColumn('calories', $cals);//绑定方法2             //while判断条件:先将按行获取函数执行的结果赋值给$row,再对$row进行判断,如果是true继续,是false退出             //由于此处fetch()函数的返回值是布尔型,因此如果成功执行就返回TRUE,如果下一行没有数据了,就返回FALSE             //fetch()函数中的参数PDO::FETCH_BOUND是一个预定义常量,代表指定获取方式。             //也就是执行成功返回TRUE,且将结果集中的列值分配给通过PDOStatement::bindColumn()方法绑定的PHP变量             //看见这种常量不要发憷,它实际上就是一个整数值,起到一个标记的作用,告诉函数应该按那种方式运行,类似于switch选择一样             //每种常量具体的名称和代表的意义可以在手册上查询             while ($row = $stmt->fetch(PDO::FETCH_BOUND))             {               $data = $name . "\t" . $colour . "\t" . $cals . "\n";               print $data;//如果这里不是打印,而是将$data设置成数组,是不是就可以取出数据放入数组中,然后在其他的代码中用             }            }        catch (PDOException $e) {             print $e->getMessage();        }    }```

3.3 PDOStatement::bindParam()

作用:绑定一个参数到指定的变量名。
绑定一个PHP变量到用作预处理的SQL语句中的对应命名占位符或问号占位符。
不同于PDOStatement::bindValue(),此变量作为引用被绑定,并只在PDOStatement::execute()被调用的时候才取其值。
大多数参数是输入参数,即,参数以只读的方式用来建立查询
参数
mixed parameter:必须参数。参数标识符。使用命名占位符,应是类似 :name 形式的参数名。使用问号占位符的,应是以1开始索引的参数位置。
mixed variable:必须参数。绑定到 SQL 语句参数的 PHP 变量名。
int data_type:可选参数。使用 PDO::PARAM_ 常量明确地指定参数的类型。
int length:可选参数。数据类型的长度。为表明参数是一个存储过程的 OUT 参数,必须明确地设置此长度。
mixed driver_options:可选参数。
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。
用法示例
*注意
我觉得这个示例很好的说明了bindParam()bindValue()之间的区别,因为这个函数绑定的是引用,因此变量的修改可以带入到里面。
直接将’%‘放入SQL语句可能会报错,而通过引用的方式,将’%‘通过字符串连接的方式复制给变量,达到预期的目的。

    // 通过GET传参的方式,获取外部传过来的需要查询的字符串     $keyword = $_GET['keyword'];     // 准备预处理对象     $sth = $pdo->prepare('SELECT * FROM `users` WHERE `firstname` LIKE :keyword');     // 将%通过字符串连接赋值给变量     $keyword = "%".$keyword."%";     // 将变量和查询的条件进行绑定,PDO::PARAM_STR,表示SQL中的CHAR、VARCHAR或其他字符串类型     $sth->bindParam(':keyword', $keyword, PDO::PARAM_STR);

3.4 PDOStatement::bindValue()

作用:把一个值绑定到一个参数
参数
mixed parameter:参数标识符。对于使用命名占位符的,应是类似 :name 形式的参数名。对于使用问号占位符的,应是以1开始索引的参数位置。
mixed value:绑定到参数的值
data_type:使用 PDO::PARAM_ 常量明确地指定参数的类型
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。
*用法示例

    /* 通过绑定的 PHP 变量执行一条预处理语句 */     $calories = 150;     $colour = 'red';    //这里是假设查询fruit表中的name,colour,calories三个字段,查询条件是calories小于150,并且颜色是红色     $sth = $pdo->prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour');     $sth->bindValue(':calories', $calories, PDO::PARAM_INT);//这种写法也许看不出和绑定参数的区别     $sth->bindValue(':colour', 'red', PDO::PARAM_STR);//**注意**这种写法,就是’red‘的位置,这个函数可以直接将变量的值放在函数里面的     $sth->execute();

3.5 PDOStatement::setFetchMode()

作用:为语句设置默认的获取模式,注意这个函数只是设置模式,并不代表已经输出。
参数
mode:获取模式必须是 PDO::FETCH_ 系列常量中的一个。
colno:列号。
classname:类名。
ctorargs:构造函数参数。
object:对象。
注意这里的参数比较有意思,它是分情况使用的
情况1:PDOStatement::setFetchMode ( int $mode ):
情况2:PDOStatement::setFetchMode ( int $PDO::FETCH_COLUMN , int $colno )
PDO::FETCH_COLUMN表示从结果集中的下一行返回所需要的那一列。那返回哪一列呢,就是$colno代表的列啦。
情况3:PDOStatement::setFetchMode ( int $PDO::FETCH_CLASS , string $classname , array $ctorargs )
PDO::FETCH_CLASS表示创建一个所请求的$classname类的新实例,并将$ctorargs作为构造函数的参数映射列到类中对应的属性名。
情况4:PDOStatement::setFetchMode ( int $PDO::FETCH_INTO , object $object )
PDO::FETCH_INTO表示更新一个请求类的现有实例,映射列到已经实例化的$object类中对应的属性名。
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。
*用法示例

    //这是情况1的例子,按照PDO::FETCH_NUM指定的获取方式,也就是将对应结果集中的每一行作为一个由列号索引的数组返回,从第 0 列开始    $sql = 'SELECT name, colour, calories FROM fruit';    try {      $stmt = $pdo->query($sql);      $result = $stmt->setFetchMode(PDO::FETCH_NUM);      //这里的$row是一个索引数组      while ($row = $stmt->fetch()) {        print $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n";      }    }    catch (PDOException $e) {      print $e->getMessage();    }    //这是情况3的例子,PDO::FETCH_PROPS_LATE代表设置属性前调用构造函数    $stmt = $pdo->prepare("你自己设定的查询字符串");    //"className"表示你要新实例化的那个类的名称,$constructorArguments是你实例化时要传入的构造函数的参数数组    $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "className", $constructorArguments);    $stmt->execute();    //我猜测在这里$stmt应该成为了你实例化的那个对象了    foreach ($stmt as $row)    {        // 这里就像你操作对象那样操作了    }

3.6 PDOStatement::fetchAll()

作用:返回一个包含结果集中所有行的数组。
注意手册中指出:使用此方法获取大结果集将导致系统负担加重且可能占用大量网络资源。
与其取回所有数据后用PHP来操作,倒不如考虑使用数据库服务来处理结果集。
例如,在取回数据并通过PHP处理前,在 SQL 中使用 WHERE 和 ORDER BY 子句来限定结果。
我在寒假大作业中用了这个方法,笨啊~
参数
fetch_style: 控制返回数组的内容。默认为 PDO::ATTR_DEFAULT_FETCH_MODE 的值( 其缺省值为 PDO::FETCH_BOTH )
想要返回一个包含结果集中单独一列所有值的数组,需要指定 PDO::FETCH_COLUMN 。通过指定 column-index 参数获取想要的列。
想要获取结果集中单独一列的唯一值,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_UNIQUE 按位或。
想要返回一个根据指定列把值分组后的关联数组,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_GROUP 按位或。
fetch_argument:可选参数。根据 fetch_style 参数的值,此参数有不同的意义:

PDO::FETCH_COLUMN:返回指定以0开始索引的列。
PDO::FETCH_CLASS:返回指定类的实例,映射每行的列到类中对应的属性名。
PDO::FETCH_FUNC:将每行的列作为参数传递给指定的函数,并返回调用函数后的结果。
ctor_args:当 fetch_style 参数为 PDO::FETCH_CLASS 时,自定义类的构造函数的参数。
返回值:返回一个包含结果集中所有剩余行的数组。此数组的每一行要么是一个列值的数组,要么是属性对应每个列名的一个对象。
用法示例:(手册上有更多的示例)

    $sth = $pdo->prepare("SELECT name, colour FROM fruit");    $sth->execute();    /* 获取第一列所有值 */    $result = $sth->fetchAll(PDO::FETCH_COLUMN, 0);//如果这里没有参数,那将返回整个查询结果的数组    var_dump($result);

3.7 PDOStatement::fetch:

作用:从结果集中获取下一行
参数
fetch_style:控制下一行如何返回给调用者。
此值必须是 PDO::FETCH* 系列常量中的一个,缺省为 PDO::ATTR_DEFAULT_FETCH_MODE 的值 (默认为 PDO::FETCH_BOTH )。
PDO::FETCH_ASSOC:返回一个索引为结果集列名的数组。
PDO::FETCH_BOTH(默认):返回一个索引为结果集列名和以0开始的列号的数组。
PDO::FETCH_BOUND:返回 TRUE ,并分配结果集中的列值给 PDOStatement::bindColumn() 方法绑定的 PHP 变量。
PDO::FETCH_CLASS:返回一个请求类的新实例,映射结果集中的列名到类中对应的属性名。
PDO::FETCH_INTO:更新一个被请求类已存在的实例,映射结果集中的列到类中命名的属性。
PDO::FETCH_LAZY:结合使用 PDO::FETCH_BOTH 和 PDO::FETCH_OBJ,创建供用来访问的对象变量名.
PDO::FETCH_NUM:返回一个索引为以0开始的结果集列号的数组。
PDO::FETCH_OBJ:返回一个属性名对应结果集列名的匿名对象。
cursor_orientation:对于 一个 PDOStatement 对象表示的可滚动游标,该值决定了哪一行将被返回给调用者。
此值必须是 PDO::FETCH_ORI
系列常量中的一个,默认为 PDO::FETCH_ORI_NEXT。
要想让PDOStatement对象使用可滚动游标,必须在用PDO::prepare()预处理SQL语句时,设置PDO::ATTR_CURSOR属性为PDO::CURSOR_SCROLL。
offset:
对于一个 cursor_orientation 参数设置为 PDO::FETCH_ORI_ABS的PDOStatement对象代表的可滚动游标,此值指定结果集中想要获取行的绝对行号。
对于一个 cursor_orientation 参数设置为 PDO::FETCH_ORI_REL 的PDOStatement 对象代表的可滚动游标,此值指定想要获取行相对于调用 PDOStatement::fetch() 前游标的位置
返回值:成功时返回的值依赖于提取类型。在所有情况下,失败都返回 FALSE 。
*用法示例

    $sth = $pdo->prepare("SELECT name, colour FROM fruit");    $sth->execute();    /* 运用 PDOStatement::fetch 风格 */    print("PDO::FETCH_ASSOC: ");    print("Return next row as an array indexed by column name\n");    $result = $sth->fetch(PDO::FETCH_ASSOC);    print_r($result);    print("\n");    print("PDO::FETCH_BOTH: ");    print("Return next row as an array indexed by both column name and number\n");    $result = $sth->fetch(PDO::FETCH_BOTH);    print_r($result);    print("\n");    print("PDO::FETCH_LAZY: ");    print("Return next row as an anonymous object with column names as properties\n");    $result = $sth->fetch(PDO::FETCH_LAZY);    print_r($result);    print("\n");    print("PDO::FETCH_OBJ: ");    print("Return next row as an anonymous object with column names as properties\n");    $result = $sth->fetch(PDO::FETCH_OBJ);    print $result->NAME;    print("\n");

3.8 其他方法

以下方法下次再来细说
PDOStatement::fetchObject — 获取下一行并作为一个对象返回。
PDOStatement::columnCount — 返回结果集中的列数
PDOStatement::fetchColumn — 从结果集中的下一行返回单独的一列。
PDOStatement::getColumnMeta — 返回结果集中一列的元数据
PDOStatement::closeCursor — 关闭游标,使语句能再次被执行。
PDOStatement::rowCount — 返回受上一个 SQL 语句影响的行数
PDOStatement::getAttribute — 检索一个语句属性
PDOStatement::setAttribute — 设置一个语句属性
PDOStatement::nextRowset — 在一个多行集语句句柄中推进到下一个行集
PDOStatement::debugDumpParams — 打印一条 SQL 预处理命令
PDOStatement::errorCode — 获取跟上一次语句句柄操作相关的 SQLSTATE
PDOStatement::errorInfo — 获取跟上一次语句句柄操作相关的扩展错误信息

4.在网上找到的一个别人的总结(这段有版权声明)

4.1 连接数据库

PDO 连接数据只需new一个PDO对象并传入相关配置,包括数据库驱动,主机,端口,数据库名,链接帐号和密码等。
若连接失败会抛出 PDOException。

    try {    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'account', 'password');    } catch (PDOException $e) {    echo $e->getMessage();    }

4.2 查询

使用 query 方法来执行查询语句,该方法返回一个 PDOStatement 对象。
可以遍历该对象获得查询结果也可以用该对象的 fetch 或 fetchAll 方法获得查询结果。

    $sta = $pdo->query('select * from table');    foreach($sta as $row) {    print_r($row);    }    $rows = $sta->fetchAll(PDO::FETCH_BOTH);    foreach($rows as $row) {    print_r($row);    }    print_r($sta->fetch(PDO::FETCH_BOTH, 0));

4.3 增删改

使用 exec 方法来执行增删改操作,该方法返回操作影响的行数。
要获得增加操作的自增id可以使用 lastInsertId方法。
$num = $pdo->exec('insert into table values (1)');
echo $pdo->lastInsertId();

4.4 准备查询

在需要用不同参数对多次执行一个查询时,例如多次修改数据时,准备查询可以提高效率。
它是将 sql 语句编译成一个模版,每次执行时只替换模版中的参数。
PDO 通过 prepare方法编译一个 SQL 模版,SQL 模版中可以用 ? 表示一个参数,也可以用 :参数名 表示一个有名字的参数。
prepare返回一个PDOStatement对象,通过它的bindParam方法设置参数,使用execute方法执行SQL模版,execute方法有一个可选的数组参数,也可以在这里用数组设置模版参数。

    $sta = $pdo->prepare('select * from table where name = ?'); //准备 SQL 模版,其中 ? 代表一个参数。    $sta->execute(array('name1')); //通过数组设置参数,执行 SQL 模版    $sta->execue(array('name2'));     print_r($sta->fetchAll());    $sta = $pdo->prepare('update table set name = 'newName' where name = :name'); //准备 SQL 模版,其中 :name 代表一个有名字的参数    $sta->execute(array(':name'=>'name1')); //通过数组设置参数,执行 SQL 模版    $sta->bindParam(':name', 'name2'); //通过bindParam设置参数    $sta->execute(); echo $sta->columnCount(); //columnCount 方法返回查询结果的行数  $sta->rowCount(); // 返回操作影响的行数

4.5 错误处理

设置 PDO::ATTR_ERRORMODE 可以修改 PDO 错误处理模式。可选的模式有:
PDO::ERRORMODE_EXCEPTION 发生错误时抛出异常。
PDO::ERRORMODE_SILENCE 默认设置 发生错误时不进行任何处理。
PDO::ERRMODE_WARING 发生错误时生成一条警告消息。
同时我们可以通过 PDO 的 errorCode() 方法来获取标准 SQL 错误代码,通过 errorInfo() 方法获取错误消息。

    $pdo->setAttribute(PDO::ATTR_ERRORMODE, PDO::ERRORMODE_EXCEPTION); //让 PDO 在发生错误时抛出异常    $pdo->exec('insert into table values (1)');    echo $pdo->errorCode(); //获得标准 SQL 错误码    echo $pdo->errorInfo()[2]; //获得错误信息 errorInfo 返回一个数组,0 位存 SQL 错误码;1 位存数据库驱动错误码;2 位存错误消息

4.6 原作者的版权声明

版权声明:本文为CSDN博主「Mir2」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zaqwsx20/article/details/64127619

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议