我使用API
开发了一个SLIM
,效果很好,但是很不幸,我在PDO
连接上遇到了问题。本质上,在调用REST-API
时遵循以下逻辑:
1. Route call controller class
2. Controller class call the model
例如:
$this->post('/user/add', \User::class . ':add');
以上路线将在类add
上调用方法User
:
<?php namespace App\Controllers;
defined('BASEPATH') OR exit('No direct script access allowed');
use Slim\Http\Request;
use Slim\Http\Response;
use Core\Controller;
use App\Models\userModel;
use Exception;
class User extends Controller
{
private $_user_model;
public function __construct($settings)
{
parent::__construct($settings);
$this->_user_model = new UserModel($settings);
}
public function add(Request $request, Response $response, array $args)
{
$data = $request->getParsedBody();
$result = $this->_user_model->add($data['user']);
return $response->withJSON([
"status" => SUCCESS_MSG,
"data" => json_encode($result, true),
"message" => "User stored."
]);
}
}
重要的部分在UserModel
中:
<?php namespace App\Models;
defined('BASEPATH') OR exit('No direct script access allowed');
use App\Controllers\License;
use Core\Model;
use Exception;
use PDO;
class UserModel extends Model
{
public function __construct($settings)
{
parent::__construct($settingss);
}
您可以看到该类扩展了Model
并实例化了一个新连接,问题在于,当API
返回响应时,该连接未关闭:
<?php namespace Core;
defined('BASEPATH') OR exit('No direct script access allowed');
use Core\Database;
class Model
{
function __construct($settings, $tenant = false)
{
$this->db = new Database($settings, $tenant);
}
function __destruct()
{
$this->db = null;
}
}
Model
类包含db
一部分的Database
对象,还有一个destructor
可以取消连接:
<?php namespace Core;
defined('BASEPATH') OR exit('No direct script access allowed');
use PDO;
class Database extends PDO
{
public function __construct($settings, $tenant = false)
{
try
{
$options = [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
PDO::ATTR_PERSISTENT, false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => ($settings['displayErrorDetails']) ? PDO::ERRMODE_EXCEPTION : PDO::ERRMODE_SILENT
];
$db = $settings['db'];
parent::__construct($db['type'] . ':host=' . $db['host'] . ';dbname=' . $db['name'], $db['user'], $db['pass'], $options);
}
catch(PDOException $e)
{
throw $e->getMessage();
}
}
}
如您所见,我将ATTR_PERSISTENT
设置为false。
每次调用API
时都会创建一个新连接,但不会释放该连接,如果我执行,则实际上保持不变:
show processlist
我有:
我的代码有问题吗?这个问题实在令人讨厌,因为TOO MANY CONNECTION
被多次调用会得到API
。
有什么解决办法吗?
更新
我看到只有在API中有错误时才会发生问题,我实际上使用Container
处理错误:
$container['errorHandler'] = function ($c) {
return new \Core\ErrorHandler($c);
};
这是完整的课程:
<?php namespace Core;
defined('BASEPATH') OR exit('No direct script access allowed');
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class ErrorHandler extends \Slim\Handlers\Error
{
private $_settings;
public function __construct($c)
{
$this->_settings = $c['settings'];
}
public function __invoke(Request $request, Response $response, \Exception $exception)
{
$status = $exception->getCode() : 500;
$error = [
"status" => ERROR_MSG,
"data" => [
"stack_trace" => ($this->_settings['displayErrorDetails']) ? $exception->getTraceAsString() : '',
],
"message" => $exception->getMessage()
];
$body = json_encode($error, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
return $response
->withStatus($status)
->withHeader("Content-type", "application/json")
->write($body);
}
}
由于某种原因,应用程序实例仍处于打开状态,并且连接也保持打开状态
答案 0 :(得分:1)
我在这里看到两个问题。
第一:
PDO::ATTR_PERSISTENT, false,
PDO::ATTR_PERSISTENT
和false
之间有一个逗号,而不是=>
。这可能会导致意外的行为。至少,它不会像预期的那样将ATTR_PERSISTENT
设置为false。
第二:
function __construct($settings, $tenant = false)
{
$this->db = new Database($settings, $tenant);
}
每次创建Model对象时,您都在创建一个新的Database对象-从而创建了到数据库的新连接。毫不奇怪,这会创建许多数据库连接。
不要这样做。 Database对象应该作为一个单例存在,可能作为应用程序全局或框架类的静态属性存在。不应常规实例化它。
答案 1 :(得分:1)
在Slim中,应将容器用作应用程序服务(如数据库连接)的“工厂”。请不要从PDO扩展类,因为它会导致很多奇怪的错误。
为共享数据库连接对象创建容器条目,如下所示:
(Slim 3示例)
// container.php
use Slim\Container;
use PDO
// ...
$container[PDO::class] = function (Container $container) {
$settings = $container->get('settings')['db'];
$host = $settings['host'];
$dbname = $settings['database'];
$username = $settings['username'];
$password = $settings['password'];
$charset = $settings['charset'];
$collate = $settings['collation'];
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset COLLATE $collate"
];
return new PDO($dsn, $username, $password, $options);
};
// ...
然后创建具有数据库连接(PDO)的模型(数据映射器)作为依赖项,如下所示:
namespace App\Model
use PDO;
class UserModel
{
private $connection;
public function __construct(PDO $connection)
{
$this->connection = $connection;
}
public function findAllUsers(): array
{
$this->connection->query('SELECT * FROM users')->fetchAll();
}
}
您不需要析构函数,因为在请求完成后,PHP会自动为您关闭连接。