当前位置: 首页 > 后端技术 > PHP

(只适合)PDO教程

时间:2023-03-30 02:30:57 PHP

什么是PDO首先想到,为什么选择PDOPDO是一个数据访问抽象层(DatabaseAccessAbstractionLayer)。抽象是双重的:一个是众所周知但不太重要的。另一个是模糊但最重要的。众所周知,PDO是为不同的数据库提供统一的接口。虽然这个功能本身很大,但是对于固定的程序来说并不是太重要。基本上所有的程序都使用统一的后端数据库。尽管有一些谣言,但通过更改单行PDO配置来切换后端数据库是不可能的——由于不同的SQL样式(为此,需要像DQL这样的普通查询语言)。所以对于普通的LAMP开发人员来说这是微不足道的,而对于他来说,PDO只是熟悉的mysql(i)_query()函数的另一个更复杂的版本。但其实不然,它还有其他丰富的功能。PDO不仅抽象了数据库API,还抽象了基本操作,否则在每个应用程序中必须重复数百次,使您的代码非常WET。与mysql和mysqli都不能直接使用低级裸API(而只能作为一些更高级别抽象层的构建材料)不同,PDO就是这样一种抽象。仍然不完整,但至少可用。真正的PDO好处是:安全性(准备好的语句可用)可用性(许多帮助函数自动执行日常操作)可重用性(对于访问大型数据库的统一API很有用,从SQLITE到oracle)请注意,尽管PDO是本机数据库驱动程序中最好的,对于现代web应用程序,请考虑将ORM与查询构建器或其他更高抽象级别的库一起使用,并且仅偶尔使用本机PDO。好的ORM,例如Doctrine、Eloquent、RedBean和Yii::AR。Aura.SQL是使用具有许多附加功能的PDO包装器的一个很好的例子。无论哪种方式,最好先了解基本工具。那么,让我们开始吧:连接DSNPDO有一个称为DSN的预测连接。它并不复杂-PDO要求您在三个不同的地方输入不同的配置,并且不是一个简单的选项列表。数据库驱动程序、主机、db(schema)名称和字符集,以及不常用的端口和unix_socket设置DSN用户名和密码设置构造方法其他所有配置都在options数组中,其中DSN是一个分号分隔的字符串,由param=value键值对组成,从驱动名和一个冒号开始:mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4driver^^colon^param=valuepair^semicolon注意,遵循正确的格式非常重要-DSN中不能使用空格、引号和其他符号,只能使用参数、值、和定义分隔符。如说明书所示。这是一个例子:$host='127.0.0.1';$db='test';$pass='root';$charset='utf8mb4';$dsn="mysql:host={$host};dbnamej={$db};charset={$charset}";$options=[PDO::ATR_ERRMODE=>PDO::ERRMODE_EXECPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,PDO::ATTR_EMULATE_PREPARES=>false,];尝试{$pdo=new\PDO($dsn,$user,$pass,$options);}catch(\PDOException$e){}设置好以上所有变量属性后,我们将在$pdo中得到一个正确的PDO实例多变的。使用旧的mysql扩展用户重要注意事项与可以在代码的任何地方使用的mysql_*函数不同,pdo实例存储在一个变量中,这意味着它只能在函数内部访问时使用。因此,您必须传递函数参数或使用更高级的技术,例如IOC容器。连接只创建一次。不要在函数、类构造函数中创建连接,否则会创建多个连接,最终导致数据库服务Crash。所以必须只创建PDO实例,让整个脚本使用。(适用于FPM模式)通过DSN设置字符集很重要——这是唯一正确的方式,因为它会告诉PDO将使用哪个字符集使用。因此,忘记通过查询或通过PDO::MYSQL_ATTR_INIT_COMMAND运行SETNAMES。如果PHP版本太低(低于5.3.6),只使用SETNAMES查询,并关闭仿真模式。关于连接的更多信息您可以通过连接到MySQLPDO::query()查看正在运行的查询有两种使用PDO运行查询的方法。如果查询中没有使用变量,则可以使用PDO::query方法。它将运行查询并返回一个PDOStatement类的对象,该类与mysql_query返回的资源大致相同,特别是从中获取实际记录的操作:$stmt=$pdo->query('SELECT名称来自用户');while($row=$stmt->fetch()){echo$row['name']."\n";}和query()方法允许我们以一种简洁的方式连接SELECT查询,如下所示。预处理,防止SQL注入放弃熟悉的mysql_query()函数,进入strictDataObjects字段的主要原因是PDO开箱即用的preparedstatements。如果你想在语句中使用变量,准备好的语句是让它正常工作的唯一方法。它如此重要的原因在于《TheHitchhiker'sGuidetoSQLInjectionprevention》。有详细的解释。对于运行查询,如果至少使用一个变量,则必须用占位符替换它。准备要执行的语句,然后传入变量单独执行。长话短说,它并不像感觉的那么困难。在大多数情况下,您只需要使用函数prepare和execute。首先,您需要修改查询,在使用变量的地方添加占位符,例如$sql="SELECT*FROMusersWHEREemail='{$email}'ANDstatus='{$status}'";更改为$sql="SELECT*FROMuserswhereemail=?andstatus=?";或$sql="SELECT*FROMuserswhereemail=:emailANDstatus=:status";请注意,PDO支持位置(?)和命名(:email)占位符,后者始终以冒号开头,并且只能使用字母、数字和下划线。另请注意,您不能在占位符周围使用引号。查询如果使用占位符,则必须使用PDO::prepare()方法进行预处理。该方法返回一个PDOStatement对象,与我们上面讨论的相同,但不绑定任何数据。最后,您必须使用execute(PDOStatement对象的)方法执行查询,并以数组的形式传递参数。之后,可以从语句中获取结果数据(如果适用)。$stmt=$pdo->prepare("SELECT*来自用户WHERESemail=?ANDstatus="");$stmt->execute([$email,$status])$user=$stmt->fetch();//or$stmt=$pdo->prepare("SELECT*FROMusersWHEREemail=:emailANDstatus=:status");$stmt->execute(['email'=>$email,'status'=>$status]);$user=$stmt->fetch();可以看到,location占位符r,你需要提供一个索引数组。命名占位符,您需要提供一个关联数组,并且键必须与查询中的占位符匹配。您不能在同一个查询中混合使用位置占位符和命名占位符。位置占位符允许您编写更短的代码,但对参数的顺序敏感(必须与查询中参数的顺序一致)。尽管命名占位符使代码更加冗长,但允许随机参数绑定。另请注意,虽然存在常见的误解,但数组中的key:并不是必需的。执行后,您可以使用支持的方法获取结果。更多示例可以查看(相应文章)[https://phpdelusions.net/pdo_...]。参数绑定将数据传递到execute()(如上所示)方法应被视为默认且最方便的方法。如果使用这种方式,所有的参数都会被绑定为字符串(如果使用NULL值,则会使用SQLNULL发送给查询),大部分时候是没有问题的。然而,有时最好明确设置类型。可能会出现以下情况:开启模拟模式的LIMIT子句(或其他不能接受字符串操作数的SQL子句)可能会受到错误操作数类型的影响具有特殊查询计划的复杂特殊查询列类型,如bigintboolean必须绑定到精确的操作数(为了绑定BIGINT,因为PDO::PARAM_INT需要基于mysqlnd)在这种情况下,必须使用显式绑定,可以从bindvalue()和bindParam()这两个函数中选择一个。推荐前者,它不像bindParam()那样有一定的副作用。可以绑定的查询部分对于了解查询的哪些部分可以使用参数绑定而哪些部分不能使用非常重要。事实上,列表很短:只能绑定字符串和数字文字。只要您的数据在查询中可以表示为数字或带引号的字符串,就可以绑定。在所有其他情况下,您不能使用PDO准备语句:无论是标识符还是逗号分隔列表,或引用文字字符串的一部分,或任何其他任意查询部分都不能使用准备语句绑定最常见的用例解决方案可以在[本章部分视图的响应]()预处理,多次执行有时你可以使用预处理多次执行一个准备好的查询,这比一遍又一遍地执行同一个查询要快一点,因为它只解析查询一次。如果有可能在实例中执行另一个PHPPrepared语句,这个特性就非常有用,但事实并非如此。它只会在同一个实例中重复同一个查询,这在常规的PHP脚本中很少使用,并且限制了使用此功能重复插入和更新。$data=[1=>1000,2=>200,3=>200,];$stmt=$pdo->prepare('UPDATEusersSETbonus=bonus+?whereid=?');foreach($dataas$id=>$bonus){$stmt->execute([$bonus,$id]);}请注意,此功能有点被高估了。它不仅需要讨论,而且性能提升也不大——查询解析有时非常快。并且只有在关闭仿真模式时才能实现性能提升。运行SELECTINSERTUPDATEDELETE语句这些查询没有什么特别的否则,它们对于PDO都是相同的。运行哪个查询并不重要。如上所示,您需要准备一个带有占位符的预处理查询,传入变量并执行它。DELETE和SELECT的处理基本相同。唯一的区别是(DML查询不会返回任何数据),您可以使用链式方法,调用execute()和prepare()。$sql="UPDATEusersSETname=?whereid=?";$pdo->prepare($sql)->execute([$name,$id]);但是,如果你想获得受影响的行数,代码将与无聊的三行代码相同:$stmt=$pdo->prepare("DELETEFROMgoodswherecategory=?");$stmt->执行([$cat]);$deleted=$stmt->rowCount();更多示例可以在相应的文章中找到。从foreach的语句数据中获取我们已经看到了这个函数,现在让我们仔细看看。它从数据库中获取单行数据,通过结果集移动一个内部指针,以便后续对该函数的调用将一一返回所有行。此方法与mysql_fetch_array()大致相同,但工作模式略有不同:不是许多不同的函数(mysql_fetch_assoc()mysql_fetch_row),此方法只有一个方法,但其行为可以通过参数更改。PDO中有很多fetch模式,我们稍后再详细讨论,这里有一些简单的例子:返回一个对象PDO::FETCH_LAZY允许三种(索引数组、关联数组、对象)方法没有内存开销。从上面可以看出,这必须在两种情况下使用:当只需要一行时,只需要一行$row=$stmt->fetch(PDO::FETCH_ASSOC);当我们需要在使用它之前处理返回的数据时,将与数组方式相关联以从语句中获取一行。在这种情况下,它必须通过一个while循环运行,如上所示。其他一个有用的模式是PDO::FETCH_CLASS可以创建特定类的对象$news=$pdo->query("select*fromnews")->fetchAll(PDO::FETCH_CLASS,'News');会生成一个News类对象数组,通过返回值设置类属性。请注意,在这种模式下:属性将在构造方法之前设置。所有未定义的属性都将调用__set魔法方法。如果没有__set方法,将创建新的属性。私有属性也会被设置,有点意外但是很方便