【问题标题】:PHP, PDO and legacy DB - change charset on the fly?PHP、PDO 和遗留数据库 - 动态更改字符集?
【发布时间】:2018-11-30 06:00:08
【问题描述】:

很久没用PHP了。需要将一个非常非常旧的站点从 mysql_* 升级到 PDO。迁移运行良好,但数据库是混合Latin1utf8 表的可怕混乱。无法改变这一点。

已经看到了一个简洁的解决方案here,您只需为每种类型的字符集创建一个连接,但我的问题是该站点是作为一个类的层次结构构建的,所有这些类都派生自一个 Db 类,它定义了字符集初始化时。

在旧代码中,问题已通过使用mysql_set_charset() “解决”,但不幸的是我找不到 PDO 等价物。

如何在 PDO 连接上动态更改字符集?甚至可能吗? 或者任何人都可以提出一种模式?

这是从 PHP 5.x 升级到 7.2 引起的“发烧救援”,它不是重构整个代码库或数据库的选项。


它或多或少是这样的:

class Db {
  private $pdo;

  public function __construct() {  
    $dsn = "mysql:host=".$this->hostname.";dbname=".$this->database.";charset=".$this->charset;

    $opt = [
      PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
      PDO::ATTR_EMULATE_PREPARES   => false
    ];

    try {
      $this->pdo = new PDO($dsn, $this->username, $this->password, $opt);
    } catch(PDOException $e) {
      echo "Error connecting to database: ". $e->getMessage();
    }
  }
}

class AnotherClass extends Db {
  public function __construct() {  
    parent::__construct();
    ...
  }
}

class YetAnotherClass extends AnotherClass [
  ...
}

想在Db 类上实现一个方法,以便我在继承的类中可以执行例如$this->changeCharset('Latin1');

【问题讨论】:

  • 是否会通过 __construct() 提供字符集作为参数帮助或在您的情况下有意义?
  • 这其实是一个更好的主意!!这是一堆源自相同基类的类,并且它是用作“前端”-PHP 和许多 AJAX 后端的核心的相同基类。但是没有 PHP 类针对字符集不同的多个表。猜猜这是我一直在寻找的“模式”(已经是一个漫长的夜晚,PHP 很远很远)看不到树木的树林:(

标签: php pdo php-7.2


【解决方案1】:

当使用 PDO 连接 MySQL 时,明智的做法是将字符集显式设置为 utf8(当然,只有在使用 utf8 时才是字符集)。在 MySQL 或 MySQLi 扩展中,我通常会执行查询 SET NAMES utf8 来设置它。

在 PDO 中,可以在连接字符串中指定字符集:

$conn = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);

charset 选项仅从 PHP 5.3.6 开始使用,因此在运行旧版本的 PHP 时要考虑到这一点。在这种情况下,您应该在构造 PDO 对象后运行以下语句:

$conn->exec('SET NAMES utf8');

但是你不应该运行这么旧的 PHP 版本。

【讨论】:

  • 看起来很有希望。 $conn->exec('SET NAMES utf8'); 肯定会在 PHP 7.2 中工作吗?它似乎适用于 PHP 5.5.9,因此我可以在执行 $this->pdo->exec('SET NAMES Latin1'); 的基类上使用 setLatin1() 方法。这真是一团糟,只需要修复然后继续前进:)
  • @davidkonrad,是的,这就是您的问题的好转。这是我为解决我的问题所做的类似方式。
  • 我借此机会看看它是否也适用于生产环境 :) 7.2 和 PDO 中似乎没有关于此的更改。非常感谢!
【解决方案2】:

已经有一个公认的答案,但我会发布这个只是为了将我在 cmets 中的想法扩展到您的示例代码中。

class Db {
  private $pdo;

  public function __construct($charset) {  
   $dsn = "mysql:host=".$this->hostname.";dbname=".$this->database.";charset=".$charset;

   $opt = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false
   ];

   try {
    $this->pdo = new PDO($dsn, $this->username, $this->password, $opt);
   } catch(PDOException $e) {
     echo "Error connecting to database: ". $e->getMessage();
   }
  }
}

class AnotherClass extends Db {
  public function __construct() {  
    parent::__construct('latin1');
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 2013-11-01
    • 2021-08-26
    • 1970-01-01
    • 2011-10-26
    相关资源
    最近更新 更多